Recipes
Four integrations that come up again and again. Each is a thin loop over endpoints documented elsewhere; this page is about how they fit together.
Sync knowledge from a CMS
Keep a chatbot's knowledge in step with content you already publish.
- On publish, POST /v1/knowledge with the content — text inline, or a file_id from POST /v1/files.
- Store the returned dataset id against your CMS record.
- On update, POST /v1/knowledge/{id}/replace_file (uploads) or /recrawl (URLs) rather than creating a second dataset.
- On unpublish, POST /v1/knowledge/remove_from_chatbot.
- Subscribe to knowledge.dataset.trained and knowledge.dataset.failed so your CMS can show whether the bot has actually learned it.
Creating a new dataset for content that already exists leaves two sources saying slightly different things, and retrieval has to pick one. Keeping the dataset id alongside your record is what makes updates possible.
Batch an initial import by sending several files at once:
curl -X POST https://api.agentency.com/v1/files \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Idempotency-Key: cms-initial-import-1" \
-F "files[]=@help/refunds.md" \
-F "files[]=@help/shipping.md"Build a support inbox
Surface conversations to your team, with the urgent ones first.
- List GET /v1/conversations — no chatbot_id gets the whole account.
- Sort your own view so handoff_requested is at the top; that is someone waiting for a person.
- Open a thread with GET /v1/conversations/{id}/messages.
- POST /v1/conversations/{id}/review when a human has dealt with it.
- POST /v1/messages/{id}/feedback to record whether the answer was good.
The part that makes it feel live is not the polling interval:
`conversation.handoff.requested` fires the moment a visitor asks for a human. Route it to whatever your team actually watches. A queue polled every minute means an average 30-second delay before anyone knows.
Mirror conversations to a warehouse
For analytics, QA, or retention rules of your own.
- Register a webhook endpoint subscribed to conversation.message.created.
- Verify the signature, then upsert data.object keyed on the message id.
- Return 200 immediately and do the write asynchronously.
- Backfill history with GET /v1/conversations plus GET /v1/conversations/{id}/messages, paging with cursors.
- Reconcile with GET /v1/events for anything missed during downtime.
Cursor pagination is stable while data is changing, so a long backfill will not duplicate or skip rows — see Pagination.
curl "https://api.agentency.com/v1/conversations?created[gte]=2026-01-01&limit=100" \
-H "Authorization: Bearer <YOUR_KEY>"Headless chat in your own UI
Your interface, your styling, the platform doing the answering.
- Your frontend calls YOUR backend. The API key never leaves your server.
- Your backend calls POST /v1/chat (or /v1/chat/stream) with an Idempotency-Key.
- Store the returned session_id against the user's conversation and send it back on the next turn.
- Render sources as citations — that is what makes the answer trustworthy.
- Handle 429 by honouring Retry-After, and MESSAGE_CREDITS_EXCEEDED by telling the user something useful.
POST/v1/chat
The key belongs on your server. A key in frontend code is public no matter how it is obfuscated. If you want chat in a page without a backend, use the widget — it is built for exactly that and carries no account credentials.
What's next
- Train a bot — the ingestion side in full.
- Conversations — everything the inbox reads.
- Webhooks — the delivery mechanism all of these use.
- Going live — before any of it carries real traffic.