Warpflow
For Developers

API Reference

Complete endpoint reference for the Warpflow Signals REST API—webhooks, AI actions, email, conversations, and config.

Base URL

https://api.warpflow.ai/api/v1

Application endpoints in the tables are prefixed with /api/v1, except /health. Most application endpoints require Bearer authentication. Health, public documentation, /api/v1/public/*, and the location_id session flow are exceptions; custom events, assistant tools, and provider webhooks use their own credentials—see Authentication.

Interactive API docs

Explore and test the documented endpoints in the browser using the interactive API documentation:

Open Interactive API Docs →

The interactive docs (Scalar) let you authorize with the credential supported by each endpoint and make live requests against the API. Use them to explore request/response schemas, try out endpoints, and generate code snippets.

The OpenAPI spec is also available for importing into Postman, Insomnia, or code generators: Download OpenAPI spec

Credential capabilities

Published endpoints can require different credentials. Tenant API keys can access same-tenant endpoints protected by tenant scope alone, including basic contact CRUD, conversations, email send, aliases GET, and tasks. Configuration writes, Signal Guard, SMS filters, and email settings writes require manager or higher rights through Cognito ID tokens or the operator admin key. Usage reads and tenant scaffolding require admin rights; usage writes require superuser rights. Embedded session tokens have agent rights. Custom events use a source webhook key, the Telnyx assistant tool uses an assistant secret, and other provider webhooks verify provider signatures.

Endpoint groups

Webhooks—Inbound event ingestion

MethodPathDescription
POST/events/{tenant_id}/{source_id}Custom webhook—any JSON payload. Uses a source webhook key; ?mode=sync evaluates rules only
POST/webhook/zapier/{tenant_id}Zapier pipeline—full classify → score → route → reply

Webhook endpoints for connected integrations are configured automatically. They use provider-specific signature verification, not Bearer auth.

AI Actions—Classify, score, generate reply

MethodPathDescription
POST/actions/zapier/{tenant_id}/classify-conversationClassify (currently unavailable: no LLM provider)
POST/actions/zapier/{tenant_id}/score-contactScore a contact with heuristic scoring
POST/actions/zapier/{tenant_id}/generate-replyGenerate (currently unavailable: no LLM provider)

Email—Send, receive, manage

MethodPathDescription
POST/tenants/{tenant_id}/email/sendSend email via connected email account
GET/tenants/{tenant_id}/email/aliasesList aliases for the connected account
PATCH/tenants/{tenant_id}/email/aliasesUpdate aliases
PATCH/tenants/{tenant_id}/email/signatureUpdate signature
POST/tenants/{tenant_id}/email/connectConnect an email account
GET/tenants/{tenant_id}/email/grantsList connected email accounts
DELETE/tenants/{tenant_id}/email/grants/{grant_id}Disconnect an email account
GET/tenants/{tenant_id}/email/grants/{grant_id}Get grant status
GET/tenants/{tenant_id}/email/auth-urlGet OAuth URL for email connection
POST/tenants/{tenant_id}/email/callbackExchange OAuth code for grant

Conversations—Multi-channel history

MethodPathDescription
GET/tenants/{tenant_id}/conversationsList conversations grouped by contact. Filter with ?alias= for email
GET/tenants/{tenant_id}/conversations/{contact_id}Full conversation history (SMS, email, voice)
POST/tenants/{tenant_id}/conversations/{contact_id}/messagesSend a message (SMS or email)
POST/tenants/{tenant_id}/conversations/{contact_id}/readMark all messages for a contact as read
POST/tenants/{tenant_id}/conversations/{contact_id}/approve-draftApprove and send a pending AI draft
POST/tenants/{tenant_id}/conversations/{contact_id}/dismiss-draftDismiss a pending AI draft without sending

Contacts

MethodPathDescription
GET/tenants/{tenant_id}/contactsList contacts for a tenant
POST/tenants/{tenant_id}/contactsCreate a new contact
GET/tenants/{tenant_id}/contacts/{contact_id}Get contact details
PATCH/tenants/{tenant_id}/contacts/{contact_id}Update contact (name, email, phone, notes)
DELETE/tenants/{tenant_id}/contacts/{contact_id}Soft-delete a contact (moves to archived)
POST/tenants/{tenant_id}/contacts/resolveResolve a contact by phone/email
POST/tenants/{tenant_id}/contacts/mergeMerge duplicate contacts
GET/tenants/{tenant_id}/contacts/{contact_id}/detailFull contact detail with scoring, source channel, and history
GET/tenants/{tenant_id}/contacts/{contact_id}/timelineUnified conversation timeline
MethodPathDescription
POST/tenants/{tenant_id}/searchSearch contacts and conversations. Warm search (6 months) by default; add deep: true for full scan

Leads

MethodPathDescription
GET/tenants/{tenant_id}/leadsList leads (filter by state, score tier, date range)
GET/leads/{lead_id}Get lead details with score and classification
GET/leads/{lead_id}/conversationFull conversation log for a lead
GET/callbacks?tenant_id={tenant_id}Deprecated task queue; use tenant tasks

Sessions

MethodPathDescription
POST/sessionsMint a short-lived, tenant-scoped session token

Request — the direct tenant_id flow requires an admin API key or authorized Cognito token:

{ "tenant_id": "acme" }

Response (200):

{
  "token": "eyJzdWIiOiJhY21lIiwibG9jIjpudWxsLCJpYXQiOjE3NzE3ODA2MDAwMDAsImV4cCI6MTc3MTc4NDIwMDAwMH0.k3Jq7f8XyR2mNvB0aL9pQwZs1TcHdEfGiJkLmNoPqRs",
  "tenant_id": "acme",
  "expires_at": 1771784200000
}

expires_at is Unix epoch milliseconds, not an ISO-8601 string. The token is two base64url segments joined by a dot — it resembles a JWT but is not one, so do not hand it to a JWT library.

The returned token is a bearer credential. Treat it like a password. Anyone holding it can act as an agent on that one tenant — reading and writing conversations, contacts and leads — until it expires. It cannot reach anything requiring manager, admin or superuser rights, and it cannot reach any other tenant. It lives for one hour from the moment it is minted, and there is no way to revoke it early: nothing is stored server-side to revoke, so a leaked token stays valid for the remainder of its hour. Mint one per session, keep it server-side or in memory, and never put it in a URL, a log, or client-side storage.

Errors: 400 BAD_REQUEST (no tenant_id, or malformed JSON), 401 UNAUTHORIZED (missing or invalid admin key), 403 FORBIDDEN (the caller has no access to that tenant), 503 SERVICE_UNAVAILABLE (session signing is not configured).

The alternative { "location_id": "YOUR_LOCATION_ID" } flow currently requires no credential; this behavior is tracked in #2237.

See Session tokens for the full walkthrough.

API Keys

MethodPathDescription
GET/tenants/{tenant_id}/api-keysList API keys (prefix only, not full key)
POST/tenants/{tenant_id}/api-keysCreate a new API key (max 3 active)
DELETE/tenants/{tenant_id}/api-keys/{key_id}Revoke an API key
POST/tenants/{tenant_id}/api-keys/rotateRotate a key (72-hour grace period)

Config

MethodPathDescription
GET/config/{tenant_id}Get tenant configuration
PUT/config/{tenant_id}Replace tenant configuration
PATCH/config/{tenant_id}Partial update tenant configuration

Webhook Subscriptions—Outbound event delivery

MethodPathDescription
GET/tenants/{tenant_id}/webhooksList webhook subscriptions
POST/tenants/{tenant_id}/webhooksCreate a webhook subscription
DELETE/tenants/{tenant_id}/webhooks/{webhook_id}Delete a webhook subscription
POST/tenants/{tenant_id}/webhooks/{webhook_id}/testSend a test event to a webhook endpoint

Calls

MethodPathDescription
GET/tenants/{tenant_id}/callsList call history
POST/tenants/{tenant_id}/calls/outboundInitiate an outbound call
POST/tenants/{tenant_id}/calls/{call_session_id}/transferTransfer an active call
GET/tenants/{tenant_id}/calls/{contact_id}/recordingGet a call recording

Tasks

MethodPathDescription
GET/tenants/{tenant_id}/tasksList task queue (status, type, limit, cursor)
PATCH/tenants/{tenant_id}/tasks/{task_id}Update task with lead_id, status/disposition/snooze

Guardrails

MethodPathDescription
GET/guardrails/{tenant_id}/activeList active Signal Guard rules
GET/guardrails/{tenant_id}/auditGet Signal Guard audit log
GET/guardrails/{tenant_id}/statsGet Signal Guard statistics
PATCH/guardrails/{tenant_id}Update Signal Guard rules
GET/guardrails/{tenant_id}/exportExport guardrail data

Pipeline—Test & process

MethodPathDescription
POST/test/{tenant_id}Dry-run a message through the pipeline (classify, score, route, reply). Nothing is sent.

SMS Filters

All SMS filter operations require manager or higher rights. DELETE accepts { "phone": "+15551234567" } and removes that one phone, returning { "status": "removed" }.

MethodPathDescription
GET/tenants/{tenant_id}/sms-filters/blocklistList blocked phone numbers
POST/tenants/{tenant_id}/sms-filters/blocklistAdd a phone number to the blocklist
DELETE/tenants/{tenant_id}/sms-filters/blocklistRemove the phone supplied in the JSON body

Billing

MethodPathDescription
GET/billing/statusGet billing status and current plan
POST/billing/activateActivate subscription (trial to paid)
POST/billing/checkoutCreate a checkout session for plan purchase
POST/billing/portalGet billing portal URL
GET/billing/credits/balanceGet credit balance
GET/billing/credits/transactionsGet credit transaction history

Additional published operations

MethodPathDescription
GET/usage/{tenant_id}Read tenant usage (admin or higher)
POST/config/{tenant_id}/scaffoldScaffold tenant configuration (admin or higher)
POST/actions/ghl/{action_key}GHL workflow dispatch; Bearer auth and extras location/contact required
POST/actions/{action_key}Legacy GHL workflow dispatch alias
GET/config/presetsList industry presets
GET/config/{tenant_id}/auditRead tenant configuration audit entries
POST/tenants/{tenant_id}/contacts/enrichedFetch enriched contacts with request filters
POST/tenants/{tenant_id}/conversations/{contact_id}/messages/subjectLabel which contact a message concerns
POST/tenants/{tenant_id}/leadsCreate a lead
GET/tenants/{tenant_id}/leads/{lead_id}Read a tenant lead
PATCH/tenants/{tenant_id}/leads/{lead_id}Update a tenant lead
POST/tenants/{tenant_id}/leads/{lead_id}/conversationAppend lead conversation entries
PUT/tenants/{tenant_id}/leads/{lead_id}/conversation/importImport lead conversation entries
POST/tenants/{tenant_id}/leads/{lead_id}/scoreRun scoring for a tenant lead
POST/tenants/{tenant_id}/leads/{lead_id}/transitionTransition a tenant lead state
POST/tenants/{tenant_id}/pipeline/batchBatch pipeline processing; tier restrictions apply

The unauthenticated health endpoint is GET /health, outside the /api/v1 prefix. GET /usage/{tenant_id} and POST /config/{tenant_id}/scaffold are tenant-admin reachable. PUT /usage/{tenant_id} is superuser-only and excluded; bare GET /config is dashboard-only, not role-gated, and excluded.

Common response patterns

Success

Success envelopes vary by endpoint. This illustrative action response does not apply to every route.

{
  "success": true,
  "data": { ... }
}

Paginated list

{
  "conversations": [ ... ],
  "next_cursor": "next_page_token"
}

This is a conversation list example. Use its next_cursor value as ?cursor=<value> for the next page.

Error

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired token"
  }
}

The HTTP status carries the status; error.code is the machine-readable value to branch on. Some errors add an error.details field with the failing fields.

See Errors for the full error code reference.

Rate limits

Limits vary by route. Email sending is limited to 50 requests per hour per tenant; public lead submissions use a separate IP-based limit. Custom events do not promise a per-tenant throttle.

The shared admin API limiter covers /config/*, /guardrails/*, /billing/*, /tenants/*, and /admin/*. Zapier pipeline and standalone Zapier actions have a webhook limiter; workflow actions use a separate limiter. Normal limiter evaluations emit X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix seconds), including on allowed requests. Store-error or skipped-key branches can omit those headers.

Some rate-limited responses include Retry-After in seconds. Use it when present; do not assume every usage-cap or throttling response supplies that header.

Pagination

Pagination varies by endpoint. Conversation lists accept cursor, use a fixed page size of 50 leads, and return next_cursor. Conversation detail currently returns next_cursor: null. Consult each endpoint schema rather than assuming every list supports limit or returns has_more.

On this page

We use cookies to understand how you use our site and improve your experience. Privacy Policy