MCP is live.Set upAsk the community
OmniDimension

Post-call actions over the API

Send every call's results to email, a webhook, Google Sheets, Slack or your CRM, and add, change or remove those deliveries from your own code.

When a call ends, your agent runs its post-call actions. Each action sends the call's results somewhere: an email report, a webhook to your server, a new row in a Google Sheet, a Slack message or a CRM record.

With the API you manage these actions one at a time. Add a webhook, and the agent's other actions stay exactly as they were, including the ones set up in the dashboard.

What your agent can send after a call

How it works

  1. 1

    Connect the account once in the dashboardOptional

    Under Integrations. One connection works for every agent in your workspace.
  2. 2

    Look up its idOptional

    GET /integrations lists your connections with their ids.
  3. 3

    Add an action to the agent

    POST one action. The agent's other actions are not touched.
  4. 4

    After every call, the action runs

    Delivered

    To the inbox, URL, sheet, channel or workflow you chose.

    Waiting for publish

    Only if the agent is published: publish it in the dashboard.

Your step Done for you

Steps 1 and 2 are only for Google Sheets, Slack, Salesforce, HubSpot and Zoho Bigin.

Before you start

  • Your API key goes in the Authorization: Bearer header.
  • For Google Sheets, Slack, Salesforce, HubSpot or Zoho Bigin, connect the account once in the dashboard under Integrations. Signing in to Google or Slack has to happen in a browser, so the API can't do that part.
  • Resellers can manage a client's agent by adding user_id. See Acting as clients.

Step by step

See the agent's actions

API reference: List post-call actions

curl "https://backend.omnidim.io/api/v1/agents/158910/post-call-actions" \
  -H "Authorization: Bearer YOUR_API_KEY"

You get every action with its id and type:

{
  "actions": [
    {
      "id": 63535,
      "type": "email",
      "recipients": ["demo@example.com"],
      "include": ["summary"],
      "extracted_variables": [],
      "trigger_call_statuses": ["completed", "voicemail_detected"]
    }
  ],
  "applies_after_publish": false
}

Add a webhook

API reference: Add a post-call action

curl -X POST "https://backend.omnidim.io/api/v1/agents/158910/post-call-actions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2d9e-0b7a-4c1e-9a55-3e2f8b1d7c40" \
  -d '{
    "type": "webhook",
    "url": "https://crm.acme-corp.io/omnidim-callback",
    "include": ["summary", "extracted_variables"],
    "extracted_variables": [
      { "key": "outcome", "prompt": "Did the caller book an appointment?" }
    ],
    "trigger_call_statuses": ["completed", "failed"]
  }'

You get the new action back, with its id:

{
  "action": {
    "id": 63536,
    "type": "webhook",
    "url": "https://crm.acme-corp.io/omnidim-callback",
    "include": ["summary", "extracted_variables"],
    "extracted_variables": [
      { "key": "outcome", "prompt": "Did the caller book an appointment?" }
    ],
    "trigger_call_statuses": ["completed", "failed"]
  },
  "applies_after_publish": false
}
  • The url must be a real, public address. Placeholder hosts like example.com, localhost and private network addresses are refused.
  • The Idempotency-Key is any unique string you choose, up to 255 characters. If your request times out and you send it again with the same key and body, you get the same action back instead of a second copy.

Check that it works

Before you point the webhook at your real server, try it with a free test inbox:

  1. Open webhook.site and copy your unique URL.
  2. Add a webhook action with that URL, as in the step above.
  3. Make a short test call to the agent and hang up.
  4. Watch webhook.site. The call data arrives shortly after the call ends, once the summary and variables are ready.

The data you see is described in What a webhook receives. When it looks right, change the action's url to your server.

Add Google Sheets, Slack or a CRM

API reference: List integrations, Add a post-call action

First, find the id of the account you connected:

curl "https://backend.omnidim.io/api/v1/integrations" \
  -H "Authorization: Bearer YOUR_API_KEY"

Pick the entry whose integration_type is google_sheets (or slack, salesforce, hubspot, zoho_bigin). Pass its id as integration_id:

curl -X POST "https://backend.omnidim.io/api/v1/agents/158910/post-call-actions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "google_sheets",
    "integration_id": 812,
    "spreadsheet_id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
    "worksheet_name": "Calls",
    "include": ["summary", "extracted_variables"],
    "extracted_variables": [
      { "key": "customer_name", "prompt": "The caller'\''s full name." }
    ]
  }'
  • The spreadsheet_id is the part of the sheet's link after /d/.
  • Each call adds one new row. The row includes the call's details, such as call_id, call_date, phone_number, to_number, from_number, call_direction, call_status, the call duration, recording_url and bot_name. Every variable extracted on the call gets its own column, from all of the agent's actions, not only this one. summary, sentiment and the full conversation are added when you include them.
  • call_date is in the agent's timezone, not UTC. See Agent timezone.
  • Bulk call campaigns add bulk_call_name, retry_count and one metadata_<key> column for each key in the contact's metadata.
  • Columns are created from the first call, and new ones are added when later calls bring new fields.

Change an action

API reference: Update a post-call action

Send only what changes. Everything else stays the same.

curl -X PATCH "https://backend.omnidim.io/api/v1/agents/158910/post-call-actions/63536" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://crm.acme-corp.io/omnidim-callback-v2" }'

An action can't change its type. To switch, remove it and add a new one.

Remove an action

API reference: Delete a post-call action

curl -X DELETE "https://backend.omnidim.io/api/v1/agents/158910/post-call-actions/63536" \
  -H "Authorization: Bearer YOUR_API_KEY"

The agent's other actions stay as they are.

What a webhook receives

After each call, a webhook action sends one POST to your url, with a JSON body like this:

{
  "call_id": 7651752,
  "call_sid": "CA4b1f9e2d7c...",
  "call_request_id": 91422,
  "bot_id": 158910,
  "bot_name": "Front desk",
  "call_direction": "outbound",
  "from_number": "+15551230000",
  "to_number": "+15551234567",
  "phone_number": "+15551230000",
  "call_date": "2026-10-16 14:02:11",
  "start_time": "2026-10-16 14:02:11",
  "end_time": "2026-10-16 14:04:37",
  "call_duration": 146,
  "call_status": "completed",
  "hangup_source": "user",
  "hangup_reason": "The caller hung up.",
  "user_email": "demo@example.com",
  "recording_url": "https://backend.omnidim.io/...",
  "recording_available_at": "2026-10-16 14:06:07",
  "low_interaction": false,
  "metadata": { "lead_id": "L-1042" },
  "call_report": {
    "summary": "The caller booked a cleaning for Friday at 10 am.",
    "sentiment": "Positive",
    "extracted_variables": { "outcome": "booked" },
    "full_conversation": "...",
    "recording_url": "https://backend.omnidim.io/...",
    "interactions": [
      {
        "sequence": 1,
        "user_query": "Hi, I want to book a cleaning.",
        "bot_response": "Sure. What day works for you?",
        "time": "2026-10-16 14:02:15"
      }
    ]
  }
}

Call fields

FieldWhat it is
call_idThe call's id in OmniDimension.
call_sidThe call's id at the phone carrier.
call_request_idThe id of the request that started the call.
bot_id, bot_nameThe agent that took the call.
call_directioninbound or outbound.
from_number, to_numberWho called whom. phone_number repeats from_number.
call_date, start_time, end_timeWhen the call ran, in UTC, as YYYY-MM-DD HH:MM:SS.
call_durationLength in seconds. Always equals end_time minus start_time.
call_statuscompleted, no-answer, busy, failed, voicemail_detected or canceled.
hangup_sourceWho ended the call: user, bot, timeout, voicemail, transfer, system, carrier or screening.
hangup_reasonWhy the call ended, in words. Most useful on failed calls.
user_emailThe email of the account that owns the agent.
recording_urlA link to the call recording, when there is one.
recording_available_atWhen the recording is ready to download.
low_interactiontrue when the call had 4 turns or fewer, for example a quick hang-up.

Every call field above is always in the body, but some can be empty. On a call with no recording or no hang-up details, such as most no-answer and busy calls:

  • recording_url, hangup_source and hangup_reason are false.
  • recording_available_at is null.

Treat both false and null as "no value".

Fields added only in certain cases

These keys are left out of the body entirely unless their condition is true.

FieldAdded whenValue
bulk_call_nameThe call was placed by a bulk call campaign.The campaign's name.
retry_countThe call was placed by a bulk call campaign.0 on the first attempt, 1 on the first retry, and so on.
extra_dataThe call was placed by a bulk call campaign.The contact's own fields from your list, or null if it has none.
metadataBulk call: always, with the contact's metadata or null. Any other call: only if you sent metadata when you started it with dispatch or a web call session.Exactly what you sent.
transferredThe agent started a transfer during the call. Never sent as false.true. It means a transfer was started, not that it connected.
transferred_to_numberA transfer was started and it had exactly one destination.That number.

The call report

call_report is always in the body. Its keys depend on include and on what the call produced:

KeyAdded when
interactionsAlways. One entry per turn of the conversation, in order. Empty when nobody spoke.
recording_urlThe call has a recording. You don't need include for it.
summaryinclude has summary, and the call produced a summary.
sentimentinclude has sentiment, and the call produced one.
extracted_variablesinclude has extracted_variables, and at least one value was extracted. One key per variable you defined.
full_conversationinclude has fullConversation, and the call has a transcript.

A call nobody answered has no conversation, so it has no summary, sentiment, extracted_variables or full_conversation, even if you asked for them. Check that a key exists before you read it.

Delivery and retries

  • Your server should answer with any 2xx status. Do the heavy work after you answer, because a request is given up after 30 seconds.
  • If your server is down, times out, or answers 408, 425, 429, 500, 502, 503 or 504, the webhook is sent again, up to 4 more times, with growing waits between tries.
  • Any other answer, such as 400 or 404, is final. It is not retried, so fix the endpoint and the next call will deliver.
  • If your server fails several times in a row, deliveries to it pause for a short while, then resume on their own. They are kept and sent, not dropped, for up to about 20 minutes.
  • Retries can send the same call twice. Use call_id to ignore a call you already processed.

Action types

typeWhat it doesYou must send
emailEmails the call reportrecipients
webhookPOSTs the call data to your URLurl
omni_crmLogs the call in Omni CRMNothing extra
cloud_whatsappSends a WhatsApp templatephone_number_id. Also send a template_id or templates, or nothing is sent.
workflowRuns one of your workflowsworkflow_id. The workflow must be published, or the action is skipped.
google_sheetsAdds a row to a sheetintegration_id, spreadsheet_id
slackPosts in a Slack channelintegration_id, plus channel_id or channel_name
salesforceCreates a Salesforce recordintegration_id (creates a Lead unless you set object_name)
hubspotCreates a HubSpot recordintegration_id, object_name
zoho_biginLogs the call in Zoho Biginintegration_id

A workflow action starts the workflow's published version with the call's details and transcript, plus the summary, sentiment and extracted variables when the call produced them. To create and publish a workflow from your code, see Build with the API. Its List workflows call gives you the workflow_id.

Every type also accepts three options:

  • include: what to send. Pick from summary, extracted_variables, fullConversation and sentiment. Zoho Bigin, workflow and WhatsApp always receive extracted variables. See How extracted variables work.
  • extracted_variables: details for the agent to pull out of the conversation, each a key and a prompt. Salesforce and HubSpot send a fixed set of fields, so they take none.
  • trigger_call_statuses: which call outcomes run the action. If you leave it out, the action runs on completed and voicemail_detected.

Every field is listed in the Add a post-call action reference.

When a change goes live

Most agents: right away. The next call uses it, and every response says "applies_after_publish": false.

Agents you publish in the dashboard: calls use the published version. The response says "applies_after_publish": true, and your change goes live the next time you publish. See Agent versioning and publishing.

Finding your action again

An action's id can change. Saving post-call changes in the dashboard, or restoring an older version, gives every action a new id.

So don't keep the id. When you need your action again, list the actions and match it by what it points at:

  • a webhook by its url
  • an email by its recipients
  • Sheets, Slack or a CRM by its integration_id

Your actions are safe from the dashboard, too. If someone has the agent open in the dashboard while you add an action, their next save keeps yours. Their own post-call change in that save is not applied: the dashboard shows the current list and asks them to make it again.

How extracted variables work

An extracted variable is a detail the agent pulls out of the conversation, like customer_name or appointment_date. Each one is a key (the name you get back) and a prompt (what the agent should look for). An action can have as many as you need.

One extraction per call. After the call, the agent reads the conversation once and fills in the variables of all the agent's actions together. Adding more actions doesn't add more work per call.

Each action gets its own keys. The results are then split by action:

ActionGets
Email, webhook, SlackOnly when include has extracted_variables. Then: the keys defined on that action, or every key extracted on the call if the action defines none.
Zoho Bigin, workflow, WhatsAppAlways. When include has extracted_variables and the action defines keys, only those keys. Otherwise, every key extracted on the call. A WhatsApp template fills its placeholders from these keys.
Google SheetsEvery key extracted on the call, from all actions, each in its own column.
Salesforce, HubSpotA fixed set of fields for the chosen object. You don't define variables for them.

Same key, same prompt. One key has one prompt per agent, because it is extracted only once. If your webhook asks for outcome and a Slack action asks for outcome too, both prompts must match. A new action that reuses a key with a different prompt is refused with a 400, and nothing changes. Give it a new key instead, such as slack_outcome.

Different keys, different prompts. Keys only need to differ from each other. You can give the webhook budget and the email follow_up_date, each with its own prompt, and each action receives its own.

Errors

StatusWhat it means
400Something in the action is wrong, like a missing field or another workspace's integration_id. Nothing is saved.
401The API key is missing or wrong.
403The key's user isn't allowed to change agents.
404The agent or action wasn't found. List the actions to find its current id.
409The Idempotency-Key was already used with a different request. Use a new key for a new request.

Every error comes back as { "error": "...", "error_description": "..." }, and the description tells you what to fix.

On this page