Skip to main content
Updated Sep 17, 2026

API Reference

What you'll learn
  • Where to find the full AutoTalk API documentation
  • How to authenticate requests with your API key
  • Key API endpoints and what they do

AutoTalk exposes a public REST API that lets external applications send messages to contacts and work with dynamic data (Dynadata). The API follows the OpenAPI 3.0 specification.

Live API documentation

The full, interactive API documentation is available at:

https://api.autotalk.io/docs

From there you can browse the documented endpoints, see request and response schemas, and try out calls directly in the browser. The published document does not cover quite everything that is live: where a route listed on this page is missing from it, this page says so on that route.

Authentication

Every request must carry one of three headers — with one exception, the public plan catalog listed below. All three headers resolve to the same company context, share the same rate limits (api_requests monthly meter), and grant access to the same endpoints — they differ only in how the token is issued.

A long-lived API key (format sk-…) created from the AutoTalk dashboard.

x-api-key: sk-YOUR_API_KEY

To generate one, go to Integrations > API Tokens and click the + button. See API Tokens for step-by-step instructions. This is the header you want for any CRM, helpdesk, script, or custom bridge calling AutoTalk from outside.

x-jwt-token (internal use by AutoTalk agents)

A short-lived JWT signed by AutoTalk and issued by the actions/security/auth/jwt/generate agent workflow action. The payload is {companyId, ips?, dur?, scopes?}; the default lifetime is 300 seconds, optionally bounded by an IP allow-list that is enforced against the caller's real public IP as seen by AutoTalk's edge (forwarded headers such as x-forwarded-for are not trusted).

Turn on advanced mode on that action to also request a scopes list, and the minted token is limited to those capabilities on /v1 and on the MCP server. A token minted without a scope list carries no scopes claim and therefore holds nothing — it authenticates, but every gate refuses it, so always list the capabilities your later steps need. A scope list supplied with advanced mode off is rejected (jwt_scopes_require_advanced) rather than silently dropped, and an unknown scope name is rejected at generation time (jwt_invalid_scopes) rather than ignored.

Every token AutoTalk signs now carries a kind claim naming what it is for, and /v1 accepts only kind: "v1" — a token issued for something else (an OAuth channel-connect redirect, for example) is rejected with 401. Tokens are minted per run and expire in minutes, so there is nothing to migrate. The requirement is unconditional: /v1 refuses a JWT whose kind claim is absent, malformed (present but not a usable string) or not "v1", on every request. There is no environment variable and no rollout step for operators to perform.

x-jwt-token: YOUR_JWT_TOKEN

Agents use this to call /v1/ on behalf of their own company — typically from a JavaScript code step (actions/code/execute with language: "javascript") that receives step(N).jwt as input. There is no user-facing endpoint to mint one; external integrators should use x-api-key instead.

A complete working example is available in Discord Spam Moderator — an actor agent that mints a JWT and uses it to delete Discord spam, warn offenders, and kick repeat violators.

x-auth-token (unified header)

A convenience header that accepts either form. Values starting with sk- are treated as API keys; other values are validated as JWTs (and if the value is ambiguous, JWT is tried first).

x-auth-token: sk-YOUR_API_KEY
# or
x-auth-token: YOUR_JWT_TOKEN

When multiple headers are present, x-jwt-token wins over x-api-key, and both win over x-auth-token.

tip

Treat all three token types like passwords. Never commit them to source control or share them in public channels. If a token is compromised, revoke it (API keys) or wait for expiration (JWTs) and rotate.

Base URL

All API requests use the following base URL:

https://api.autotalk.io/v1

Key endpoints

Below is an overview of the main API areas. For full request/response details, visit the live documentation.

Company

MethodPathDescription
GET/v1/selfRetrieve the authenticated company's profile

Contacts

MethodPathDescription
POST/v1/contacts/{contactId}/send_messageSend a message to a specific contact

Dynadata (dynamic data)

Dynadata endpoints let you manage custom data entities (contacts, orders, tickets, or any type your company defines).

MethodPathDescription
GET/v1/dynadata/typesList all available Dynadata types
POST/v1/dynadata/type/{type}/listList items of a specific type
GET/v1/dynadata/type/{type}/item/{_id}Retrieve a single item by ID
POST/v1/dynadata/type/{type}/createCreate a new item
POST/v1/dynadata/type/{type}/updateUpdate an existing item
DELETE/v1/dynadata/type/{type}/item/{_id}Delete an item by ID
POST/v1/dynadata/type/{type}/validateValidate an item without saving
GET/v1/dynadata/type/{type}/schemaGet the JSON schema for a type
GET/v1/dynadata/type/{type}/schema/zodGet the Zod schema for a type
POST/v1/dynadata/type/{type}/executeFunction/{functionName}Execute a function on a type
POST/v1/dynadata/type/{type}/item/{_id}/executeFunction/{functionName}Execute a function on a specific item
log_entries is read-only through the API

create, update and delete on the log_entries type are refused on /v1 and on the MCP server for every caller — whatever permissions the API key holds, and for an x-jwt-token too. The response is 403 with code: "log_entry_writes_not_allowed". log_entries is the audit trail the platform writes about your API traffic, so no scope grants the ability to forge or erase rows in it. Reads (list, item, schema) are unaffected.

This is a behaviour change: these writes used to succeed. If you were writing your own records into log_entries, move them to a Dynadata type you own (a custom type, for example) — create/update/delete there are unchanged. The published Discord Spam Moderator example still contains such calls; they now fail, and the example is written so a failed log is non-fatal.

Self-hosted operators can set V1_ALLOW_LOG_ENTRY_WRITES=true on the backend to restore the old behaviour while they migrate an integration. It defaults to false and is not available on the hosted platform.

Storage

Upload files (images, PDFs, audio) and get a first-party {bucket, fullPath} reference you can pass to send_message, Dynadata document fields, or functions like createWhatsappWebEvoProduct. See Uploading files.

MethodPathDescription
POST/v1/storage/upload-urlReserve a signed upload URL (step 1)
POST/v1/storage/upload-completeFinalize the upload; returns {bucket, fullPath} (step 2)
GET/v1/storage/urlGet a signed download URL, valid up to 7 days, for a stored object

Transcriptions

The standalone async transcription API (issue #822). Enqueue a transcription job for a stored audio file and poll for its status and result. Authenticated with your x-api-key.

MethodPathDescription
POST/v1/transcriptionsEnqueue a transcription job
GET/v1/transcriptions/{id}Fetch a job's status and result

Media transforms

The standalone async media transform API (issue #247). Enqueue a format conversion for a stored audio or video file, then poll for its status and output. It is the same job queue the Transform media workflow action and the get_transcode_job MCP tool use, so a job enqueued on any surface can be polled from either of the two that read it: GET /v1/transcodes/{id} and the get_transcode_job tool — the workflow action only enqueues, it has no read. Reading is where the overlap stops: canceling and retrying exist only here on /v1. The MCP server registers no cancel and no retry tool, and the job document is read-only to the tenant, so a job started from a workflow or from MCP can be canceled or retried through these two routes and nowhere else.

MethodPathDescription
POST/v1/transcodesEnqueue a transform job. fullPath and preset are required; bucket is optional and defaults to the platform storage bucket — a single bucket shared by every tenant, with your company's files under their own path prefix — so you rarely need to send it. Presets: mp4, ogg_opus, wav, flac_16k_mono
GET/v1/transcodes/{id}Fetch a job. job.output.bucket and job.output.fullPath are the converted file, and are filled in only once job.status is done
POST/v1/transcodes/{id}/cancelCancel a job that has not finished yet — pending, probing, transcoding or finalizing. A job in any other state is refused
POST/v1/transcodes/{id}/retryRe-run a failed or canceled job from scratch. Any other status is refused, as is a job whose worker lease is still in the future — every path that writes failed or canceled clears the lease, so that second condition should not normally bite, and both refusals come back as the same transcode_job_not_retryable. The request goes through the same transcoding-quota and queued-jobs checks as enqueueing, and the conversion runs and is charged again unless that same source and preset were already converted, in which case the stored result is reused and nothing is billed

The presets, the size and duration limits, the per-company queue cap and the failure codes are the same here as in the workflow action — see Transform media.

These four are not in the interactive spec yet

The published OpenAPI document behind api.autotalk.io/docs does not list the transform endpoints, so a client generated from it will not have them. The routes are live and supported; this table is the reference until the spec catches up.

Speech

Turn text into stored audio, and rewrite written text into its spoken form first. Neither endpoint returns audio bytes: POST /v1/speech stores the audio and hands back a {bucket, fullPath} reference you can pass straight to send_message, a Dynadata file field, or GET /v1/storage/url.

MethodPathDescription
POST/v1/speechSynthesize text (at most 4096 characters) into a stored audio object. Optional voiceProfileId and format. Returns the stored reference plus size, format and which meter was billed
POST/v1/spoken-textRewrite markdown (at most 8000 characters) into the spoken form of that text. Returns text only, no audio

POST /v1/speech is synchronous — the audio exists when the call returns and there is no job to poll. Markdown reads badly out loud, so run /v1/spoken-text first when the text came from an LLM or a rich-text field. Its output is not bounded in characters, though — only in tokens (1200 of them), which usually lands well under the 4096-character synthesis cap but is not guaranteed to, least of all near the 8000-character input maximum. Check the length of the rewrite before posting it to /v1/speech, and split it if it came back longer.

Usage

MethodPathDescription
GET/v1/usageThe tenant usage snapshot: meter totals, compute time broken down by category, storage and database bytes used, the active plan with its resolved limits, and subscription period info

Accepts granularity=month (the default) or granularity=day. The window is a UTC calendar period — the current month, or 00:00 UTC today with granularity=day — which is not necessarily your subscription period. The snapshot itself is the one the get_usage MCP tool returns — both surfaces read it from the same helper — but the envelopes differ: MCP hands the snapshot back bare, while /v1 wraps it as {success: true, usage}.

Workflows

Run a manual workflow first-class (issue #218) and read its durable outputs — the evaluated returnExpression as result, output files as storage refs (usable with GET /v1/storage/url), and per-step logs. Use mode: "async" for long runs (e.g. multiple Python code steps) and poll the run endpoint; pass includeStepOutputs: true to also persist per-step snapshots.

Always check ok, not the HTTP status. A step that fails does not abort the workflow, so a run can finish with broken steps: that answers status: "partial" with ok: false, an error of code workflow_steps_failed, and a failedSteps: [{step, code, userMessage}] list. Only status: "success" means the run did what it was supposed to.

When a failure is part of the design — a step whose failure a later step handles via step_error(N)/step_ok(N), or an optional side effect — tick allow failure on that step in the workflow editor. Its failure is then rolled up as {step, code, userMessage, handled: true} and reported exactly as before, but it no longer forces status: "partial": a run whose only failures are handled ones stays status: "success", ok: true. Any step failure that is NOT declared this way still makes the run partial.

MethodPathDescription
POST/v1/workflows/{id}/runRun a workflow (mode: "sync" returns {ok, result, files, logs, executionTimeMs, runId}; mode: "async" returns 202 {runId})
GET/v1/workflows/{id}/runs/{runId}Fetch a run's status and durable outputs

Public (no authentication)

MethodPathDescription
GET/v1/public/plansThe public plan catalog. Pass sellableOnly=true to return only the plans currently on sale

This is the only endpoint on this page that resolves no company and takes no auth header. Everything else needs one of the three headers above.

Like the transform endpoints above, this route is not in the published OpenAPI document either, so a client generated from it will not have this operation. The route is live; the spec has not caught up.

Example request

Here is an example of sending a text message to a contact using curl:

curl -X POST https://api.autotalk.io/v1/contacts/CONTACT_ID/send_message \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "text",
"body": {
"text": "Hello! How can we help you today?"
}
}'

Next steps

  • API Tokens -- Generate and manage your API keys
  • Webhooks -- Receive event notifications from AutoTalk
  • Workflows -- Automate tasks inside AutoTalk