Docs / Platform / How-to

API access for developers

Create API keys and manage agents and calls programmatically over a REST API.

How do I get an API key?

  1. Open Settings and find the API Access section.
  2. Create a key: give it a name and pick its scopes - agents:read, agents:write, agents:publish, agents:test, calls:read, contacts:read, contacts:write, channels:messages.
  3. Copy the key immediately. It is shown once at creation and never again; only a hash is stored. Keys can be revoked at any time from the same page.

How do I authenticate?

Send the key as a bearer token on every request. The base URL is https://app.workflowhq.co/api/v1.

curl https://app.workflowhq.co/api/v1/agents \
  -H "Authorization: Bearer YOUR_API_KEY"

What endpoints are available?

  • GET /agents - list your agents (paginated with page and limit).
  • POST /agents - create an agent. Supports an idempotency key so safe retries never create duplicates.
  • GET /agents/{id} - fetch one agent's full configuration.
  • PUT /agents/{id} - update the draft: name, voice, systemPrompt, languages, timezone, transfer destinations, agent transfers.
  • POST /agents/{id}/publish - publish the draft so live traffic picks it up.
  • POST /agents/{id}/test-call - start a test web call for the agent (limited to 5 per hour).
  • GET /calls/{id} - fetch a call record: transcript, duration, outcome, analysis, extracted fields.
  • POST /contacts - create or update contacts (upsert matched on email, then phone; batch up to 100). Returns the contact id either way.
  • GET /contacts - search by email, phone or tag, paginated with limit and offset.
  • GET /contacts/fields - the exact field keys your account accepts: the standard columns plus your own custom fields with types and dropdown options.
  • PATCH /contacts/{id} - update only the fields you send; use this when the email or phone itself is changing.
  • POST /contacts/{id}/notes - attach free text to a contact, for example a form answer or a post you scraped.
  • POST /contacts/{id}/tags - add tags without resending the record; existing tags survive, duplicates are ignored. DELETE /contacts/{id}/tags removes the named ones. Both are idempotent, so retries are safe.
  • POST /channels/{id}/messages - send a message into an API channel (see the API channel page under Integrations).
  • Prefer a ready-made website bubble? Create a Web chat widget under Settings, Integrations, Channels: you get a one-line script keyed to the widget, pick an agent to answer or none so your team replies from the Inbox, and change who answers any time without touching the site. Human-only conversations use no AI chats; an agent answering bills from your quota as usual.

How do contact upserts behave?

POST /contacts matches on email first, then phone. If a contact exists, the fields you send are merged onto it and absent fields are never blanked, so a partial payload cannot wipe data someone typed in the dashboard. If nothing matches, the contact is created. Retrying the same request is safe: it lands on the same record. Custom field values go in a custom object keyed by field key; unknown keys are rejected by name, and GET /contacts/fields tells you the keys that exist.

A first name alone is a valid contact, same rule as the dashboard. But with no email or phone there is nothing to match on, so such a request always creates - retrying it creates again. Include an email or phone whenever you mean update-or-create.

Phone numbers are stored in international form. If your account is set to India, a bare 10-digit number such as 9876543210 is saved as +919876543210, so WhatsApp and calls reach it; numbers that already carry a country code are kept as sent. Values are checked against each field's type: email fields must hold a valid address, url fields must start with http or https, and a multi-select value is sent as a JSON array - "interests": ["Gold", "VIP"] - or as a comma-separated string; both are stored the same way. A rejected value names the field and the rule, so the fix is always visible in the error.

Tags are free strings created on the fly - write signal:hiring and the tag now exists, nothing is pre-registered. One rule to know: tags in a create or update payload REPLACE the contact's whole tag list. To add tags in the same call without touching the rest, send them as addTags instead (for example "addTags": ["abandoned-cart"]); existing tags survive and duplicates are ignored. The tags endpoints do the same for a contact you already have the id of.

curl -X POST https://app.workflowhq.co/api/v1/contacts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Asha Rao",
    "email": "asha@example.com",
    "phone": "+919876543210",
    "tags": ["webinar-lead"],
    "custom": { "company": "Acme Traders" }
  }'

How do drafts and publishing work over the API?

Exactly like the dashboard: PUT edits the draft, and live phone calls and widgets keep running the last published version until you POST /publish. Every save is versioned, so changes made over the API appear in the agent's version history and can be reverted from the dashboard.

What do errors look like?

Errors return a JSON body of the shape { "error": { "code": "...", "message": "..." } } with a matching HTTP status: 401 for a missing or invalid key, 403 for a key without the required scope, 404 for a resource that doesn't exist in your organization, 422 for validation errors, and 429 when rate-limited.

Can I create workflows over the API?

Yes. GET /api/v1/automations lists your workflows, POST creates one (from a template key or a full definition in the same format the builder uses), GET /api/v1/automations/{id} returns the definition with recent runs, and PATCH edits, renames or changes status. Requires an API key with the automations read and write scopes.

Two safety rules are built in: a created workflow is always a draft, and going live is a separate PATCH that must pass the same readiness checks as the dashboard toggle. And there is no delete over the API at all: keys and AI agents can create, update and pause, while deleting anything remains a signed-in dashboard action.

Can AI assistants use my CRM directly (MCP)?

Yes. WorkflowHQ speaks MCP (Model Context Protocol), so AI tools such as Claude Code, Claude Desktop or Cursor can search your contacts, add or update them, tag them and save notes, using the same API key and permissions as the REST API. A common use: paste a LinkedIn conversation into your AI assistant and say 'save this person to my CRM with the context'.

Connect it with your MCP client's remote server option: URL https://app.workflowhq.co/api/mcp, with an Authorization: Bearer header carrying an API key that has the contacts read and write scopes. The assistant then sees six tools covering search, create or update, notes, tags and field discovery. Everything an assistant writes follows the same rules as the API: matching by email or phone updates the existing contact, and unknown custom fields are rejected.

Still stuck? Email hello@workflowhq.co - a real person replies, usually within a few hours.