Send data with a webhook
Create a webhook source, send it JSON from your own tools with curl or a signed request, add files by hand, and read every response.
A webhook source gives you a URL. Any tool or script that can send a POST request can put data into Brain through it. Only admins can create one. There is nothing to approve: the URL itself is the access.
Create a webhook source
Open the form
In the sidebar, open Sources and click Add webhook source. The Add webhook source dialog opens straight on the form, whether or not you already have webhook sources. You can also start from Connect source: the Custom webhook card has a Connect button that opens the same dialog. Before your first build, click the + button on the Custom webhook card, under Additional sources, in setup or on Home instead.
Fill in the fields
- App name: the name of the source. It labels the source in your list and becomes part of the URL. See Name rules.
- Signing secret (optional): a secret your sender uses to sign each request. See Sign requests.
Create it
Click Create. The button stays off until you type a name. Brain says "[name] connected. Send content to get started." and the dialog changes to Send content to followed by the name, with the Webhook URL. Click Copy, then Done.
The complete URL already includes the access token. Paste it as it is into the tool that will send data. Under the URL, the dialog tells you to send a POST request with Content-Type: application/json and a JSON object that holds the content, and that once your content is received it appears on Home. It links to signing instructions and payload examples, which are the sections Sign requests and What the payload can be on this page. Treat the URL like a password: anyone with it can send data to this source. Brain shows the complete URL only when it is created. During setup it stays available to copy until you reload the page. After that you can get a URL again only by generating a new one. See Regenerate the URL.
Name rules
- The name can be up to 240 characters.
- Brain turns the name into a lowercase name for the URL: letters and numbers stay, every run of other characters becomes one hyphen, and it is cut at 80 characters. "CRM events (EU)" becomes
crm-events-eu. - The name must contain at least one letter or number. Otherwise Brain says "Webhook source name must contain letters or numbers."
- The signing secret, if you set one, must be between 1 and 2000 characters.
If creating fails
Brain shows a red message that starts with "Could not create webhook source." followed by a reason.
| Reason | What to do |
|---|---|
| "Webhook source name must contain letters or numbers." | Use a name with at least one letter or number. |
| "Signing secret must contain between 1 and 2000 characters." | Shorten the secret, or leave it empty. |
| "Only organization admins can manage integrations." | Ask an admin to create it. |
| "External ingestion is paused for this organization." | Contact support. New sources cannot be created while ingestion is paused. |
| "Demo Mode prevents connecting or ingesting new external data." | Same. |
| "Webhook sources can't be created right now because of a problem on our side. Nothing was created. Contact support and we'll sort it out." | Contact support. |
| "We couldn't save this webhook source because of a problem on our side. Nothing was created. Try again in a few minutes, and contact support if it keeps failing." | Try again in a few minutes. |
| "We couldn't save the signing secret because of a problem on our side. Nothing was created. Try again in a few minutes, and contact support if it keeps failing." | Try again in a few minutes. |
Send data
Send a POST request to the URL with a JSON object as the body.
curl -X POST "https://YOUR-WEBHOOK-URL" \
-H "Content-Type: application/json" \
-d '{"event":"ticket.escalated","id":"TCK-1042","customer":"Acme","summary":"Renewal at risk after the second outage"}'
Replace https://YOUR-WEBHOOK-URL with the Webhook URL you copied. Data you send shows up in Brain as it is learned. There is no separate connect step.
A successful request answers with HTTP status 202 and a small JSON body:
{
"status": "queued",
"event": "ticket.escalated",
"resource_id": "TCK-1042",
"source_id": "src_..."
}
The body carries a few more identifiers too. "queued" means Brain has stored the request and will learn it, not that it is learned yet. Follow it on Follow ingestion.
Test a request
Add ?test=true to the URL to check your setup without sending content. Brain checks the URL, the signature if you set a signing secret, and that the body is a JSON object. It answers 202 with "status": "verified" and stores nothing: no content is added to your memory and nothing uses your usage. If you set a signing secret, sign test requests too.
curl -X POST "https://YOUR-WEBHOOK-URL?test=true" \
-H "Content-Type: application/json" \
-d '{"event":"test"}'
A test request does not make the webhook leave Waiting for content. Only a real request does. When ingestion is paused or Demo Mode is on, a test request gets the "ignored" answer too.
What the payload can be
- Any JSON object. Any keys and any nesting. Brain reads the whole object as text.
- A JSON array, a string or a number is rejected. Wrap it in an object, for example
{"items":[...]}. - Two labels are recognised, and both are optional.
event(ortypeif there is noevent) is stored as the label of the request. If neither is present, the label israw.received. - An id is recognised, and it is optional. Brain looks for the first non-empty value, in this order:
data.uuid,data.id,data.resource_id,uuid,id,resource_id. It is stored as the id of the request and helps you trace it.
Retries can arrive twice
Brain stores every request it accepts as its own delivery. If your sender retries a request that had already succeeded, for example because the answer was lost, the same payload can be stored twice. Retry only when you got an error or no answer, and keep an id in the payload so you can trace duplicates.
Sign requests
If you set a Signing secret, sign the raw request body with HMAC-SHA256 using that secret. Send the result in the X-Webhook-Signature header as sha256= followed by the hex digest. Sign exactly the bytes you send.
BODY='{"event":"ticket.escalated","id":"TCK-1042","customer":"Acme"}'
SIGNATURE=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SIGNING_SECRET" | sed 's/^.* //')
curl -X POST "https://YOUR-WEBHOOK-URL" \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: sha256=$SIGNATURE" \
-d "$BODY"
A missing or wrong signature is rejected with the signature message below. If you left the secret empty, requests are not checked for a signature.
Every response
Errors come back as JSON with a detail field holding the message. Brain checks a request in this order: the URL, then the signature, then whether ingestion is paused, then the body.
| Status | Message or body | What it means and what to do |
|---|---|---|
| 202 | "status": "queued" | Accepted. |
| 202 | "status": "verified" | A request with ?test=true passed the checks. Nothing was stored. |
| 202 | "status": "ignored" with "reason": "ingestion_paused" or "reason": "demo_mode" | Brain did not store anything because ingestion is paused for your organization or Demo Mode is on. The sender should not retry. Contact support. |
| 400 | "The request body couldn't be read as JSON. Send a JSON object." | The body is not valid JSON. |
| 400 | "Webhook payload must be a JSON object" | The body is valid JSON but not an object, for example an array. |
| 401 | "This webhook URL isn't recognized. Use the current URL from the webhook source." | The URL is wrong, was regenerated, or its source was removed. Use the current URL. |
| 401 | "The webhook signature doesn't match. Send X-Webhook-Signature as sha256=<hex HMAC-SHA256 of the raw body, keyed with the signing secret>." | The signature is missing or wrong. Sign the exact body bytes with the source's secret. |
| 404 | "Webhook source not found" | The name part of the URL is not valid. Use the URL you copied. |
| 503 | "We couldn't finish this because of a problem on our side. Your connections and data are safe. Try again in a few minutes, and contact support if it keeps failing." | Retry later. |
| 503 | "Webhooks can't be received right now because of a problem on our side. Contact support and we'll sort it out." | Retry later, then contact support. |
Retry on 503 and on network errors. Do not retry on 400, 401 or 404 until you have fixed the cause.
Send content during setup
Before your first build, the Custom webhook card sits under Additional sources in setup (the step Connect your docs, chats and tools) and on Home while it reads No sources connected yet. Click its + button, Add Custom webhook, to open a side panel. When a webhook exists, the button becomes a green check labeled Manage Custom webhook.
- The panel lists Existing webhook sources. Click View sending instructions to reopen the sending steps. If Brain no longer has the URL, for example after you reload the page, the panel says "For security, the complete URL is only shown when it is created. Copy it from the app where you configured it, or generate a new URL below." Click Generate new URL. Brain asks "Replace the URL for [name]?" and explains that it replaces the current URL, that you need to update it in your app and that requests to the old URL will no longer be accepted. Click Replace URL to go ahead.
- Send JSON lets an admin send content from the app, with no tool of your own. Paste a JSON object, up to 1 MB, and click Send JSON. Brain says "Content sent to [name]. Follow progress on Home." The form keeps your text if sending fails. It is off while ingestion is paused or Demo Mode is on.
- Done closes the panel. It waits while a send is running.
- Disconnect removes the webhook. Click it, then Confirm, or Cancel.
A webhook that has received nothing yet does not hold back Continue or Build my memory, and it does not count as content. Brain accepts what a webhook sends into your memory as soon as it arrives. You can set up other sources and come back later.
Send JSON needs a JSON object that is not empty. Brain refuses text that is not JSON ("This text could not be read as JSON. Check the quotes, commas and brackets."), an array or empty object ("Add a JSON object with the content you want Brain to read.") and text over 1 MB ("This JSON is larger than 1 MB. Send a smaller JSON object.").
Add data by hand
You can also add files without writing code.
- In Sources, click Manage on the webhook source's card.
- Under Actions, click Add data.
- In the Send files to dialog for your source, drop in or choose files. Under the drop area, a line lists the accepted formats ("md, txt, csv, json, pdf, or a ZIP archive") and links to these limits and messages.
- Click Ingest.
Brain shows the estimated usage for the files. When it is done, a message says how many documents it sent and how many it skipped as unsupported, for example "Sent 3 documents to ingest · 1 skipped (unsupported)."
| Add data (webhook source) | Upload files | |
|---|---|---|
| Formats | md, txt, csv, json, pdf, or a ZIP archive | docx, pptx, xlsx, md, txt, csv, json, pdf, or a ZIP or TAR.GZ archive |
| Size | 25 MiB combined, checked in the app before anything is sent | 256 MiB per file |
| File count | Up to 1,000 files | See Upload content |
| Confirm step | None. Ingest sends immediately. | Estimate and confirm |
| Ends up in | The webhook source you chose | The Local Uploads source |
These messages can appear:
| Message | What to do |
|---|---|
| "Choose one or more text files (md, txt, csv, json) or a ZIP." | Add a file first. |
| "Upload exceeds the 25 MiB (26,214,400 bytes) combined limit. Choose fewer or smaller files." | Send fewer or smaller files at once, or use Upload files for big ones. |
| "At most 1000 files may be uploaded." | Split the upload. |
| "These files are larger than the upload limit. Upload fewer or smaller files." | Split the upload. |
| A message naming a file, such as "(file) is password-protected. Remove the password and upload it again." | Fix that file and add it again. |
| "Brain couldn't accept these files right now. This is on our side. Try again in a few minutes, and contact support if it keeps failing." | Try again later. |
The failure is prefixed with "Could not send files." When ingestion is paused, or Demo Mode is on, Add data is off.
Regenerate the URL
If the URL leaks, replace it. In Sources, click Manage on the webhook source's card. Under Actions, click Regenerate token, then Confirm.
During setup, open the Custom webhook card, click View sending instructions on the webhook, then Generate new URL and Replace URL. In Manage, Brain shows Webhook token regenerated with the new URL. Copy it and update your tool. The old URL stops working right away: requests to it get the 401 "This webhook URL isn't recognized." answer.
Regenerating keeps the signing secret. You do not need to change your signing code, only the URL. You cannot change or read back the signing secret: to use a different one, create a new source.
If it fails, the message starts with "Could not regenerate token." Only admins can regenerate, and it is off while ingestion is paused or Demo Mode is on.
Remove a webhook source
In Manage, under Actions, click Remove source, then Confirm. The source leaves your list and its URL stops working at once: the sender gets the 401 "This webhook URL isn't recognized." answer. What Brain already learned from the source stays.
When ingestion is paused
While ingestion is paused for your organization, or Demo Mode is on, Add webhook source, Add data, Send JSON, Regenerate token and Remove source are off, and requests to existing URLs get the 202 "ignored" answer described above. Nothing sent in that time is stored.
Check that it worked
- Send a request with curl. You get 202 and
"status": "queued". A request with?test=truegets"status": "verified"instead, and stores nothing. - On Sources, the webhook source has a card. Its count of documents grows once Brain has read the request.
- On Ingest, pick the webhook source in the source filter. The request appears and moves from Queued to Learning to Learned.
Example: three sources answer one question
The value of Brain is the combined answer. Say you have a webhook source called "Support escalations" that receives an event every time your help desk escalates a ticket, plus Slack connected to your #cs-escalations channel and HubSpot connected to your deals.
Ask your assistant "Why is the Acme renewal at risk?" Brain can put together the escalation events your help desk sent, the thread in Slack where the team discussed the second outage, and the state of the Acme deal in HubSpot. No one of those sources answers it alone. The answer names the pages of Brain's memory it drew from, not the original systems. See Sources and citations.