Sign In

Integration API (/api/v1)

A small, stable surface for moving records in and out of TIA: leads, form submissions, tickets, and webhook subscriptions. This is what the Zapier app is built on, and it's designed to be equally usable from Make, n8n, a cron job, or your own backend.

API key management

Create a key, then use it from your backend

Open API keys and create a named key

Create one key per service or environment so it can be identified and revoked independently.

Copy a server-side example

Use the cURL, JavaScript, or Python request example and keep the key out of browser code.

For managing agents, knowledge sources, tools, and secrets, see the Agents API instead.

Base URL: https://tia.tdccommerce.com

Authentication

Send an API key on every request. Both forms work:

x-api-key: YOUR_API_KEY
# or
Authorization: Bearer YOUR_API_KEY

Create keys at API keys. The REST API requires the Growth plan or above.

Choosing an organization

Every scoped endpoint runs against one organization.

  • By default it uses the workspace the key was created in. Most integrations never need to think about this.
  • To override, pass ?organization=<id or slug>.

Either way, membership is re-verified server-side on every request. A valid key for one organization cannot read another by changing the parameter — it returns 403.

Call GET /api/v1/me to see which organizations a key can reach.

Conventions

FormatJSON in, JSON out.
Errors{ "error": { "message": string, "status": number } }
CachingEvery response is Cache-Control: no-store.
limitList endpoints accept limit (default 50, max 100).
sinceISO 8601 timestamp filter. Invalid values are ignored, not rejected — a stored bad value shouldn't wedge an otherwise-working integration.
OrderingLists are newest-first, so a poller can read the head and stop at the first id it has seen.

Status codes

CodeMeaning
400Malformed request or missing required field.
401Missing or invalid API key.
402Your plan doesn't include this capability.
403Valid key, but you're not a member of that organization.
404The referenced agent doesn't exist in this organization.
429Subscription limit reached.

GET /api/v1/me

Who this key is, and which workspaces it can reach. Use it as a connection test.

curl https://tia.tdccommerce.com/api/v1/me \
  -H "x-api-key: $TIA_API_KEY"
{
  "userId": "user_...",
  "organizations": [{ "id": "org_...", "name": "Acme", "slug": "acme" }],
  "boundOrganization": { "id": "org_...", "name": "Acme", "slug": "acme" }
}

boundOrganization is null if the key isn't bound to a workspace, or if the user has since left it — in which case pass ?organization= explicitly.


GET /api/v1/agents

The agents in the organization. Intended for populating a dropdown.

{
  "agents": [
    { "id": "agent_...", "name": "Support", "publicSlug": "agt_pub_8f2c" }
  ]
}

GET /api/v1/forms

The enabled custom forms in the organization, de-duplicated by name. Pass ?agentId= to scope to one agent.

{
  "forms": [
    {
      "name": "warranty_claim",
      "title": "Warranty claim",
      "agentId": "agent_...",
      "agentName": "Support"
    }
  ]
}

GET /api/v1/leads

QueryNotes
agentIdScope to one agent.
emailExact match, case-insensitive.
sinceISO 8601.
limit1–100, default 50.
{
  "leads": [
    {
      "id": "lead_...",
      "name": "Jo Rivera",
      "email": "jo@example.com",
      "phone": null,
      "contactId": "contact_...",
      "conversationId": "conv_...",
      "agentId": "agent_...",
      "agentName": "Support",
      "createdAt": "2026-08-11T09:14:22.117Z"
    }
  ]
}

POST /api/v1/leads

Push a lead in from another system. Runs the same field validation, contact resolution, and lead.created event as the in-widget form.

curl -X POST https://tia.tdccommerce.com/api/v1/leads \
  -H "x-api-key: $TIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "agentId": "agent_...", "name": "Jo Rivera", "email": "jo@example.com" }'

agentId is required. Returns 201 with the created lead.


GET /api/v1/form-submissions

QueryNotes
agentIdScope to one agent.
formNameThe form's stable key, e.g. warranty_claim.
since, limitAs above.

Field values appear both nested under values and flattened onto the submission, so integration tools can map individual fields:

{
  "submissions": [
    {
      "id": "sub_...",
      "formName": "warranty_claim",
      "values": { "order_number": "1042", "issue": "Cracked lens" },
      "order_number": "1042",
      "issue": "Cracked lens",
      "conversationId": "conv_...",
      "agentName": "Support",
      "createdAt": "2026-08-11T09:14:22.117Z"
    }
  ]
}

GET /api/v1/tickets

QueryNotes
agentIdScope to one agent.
statusnew, on_you, on_customer, on_hold, closed, cancelled.
emailRequester email, exact match.
ticketNumbere.g. TKT-1042.
sort=activityOrder by last activity instead of creation.
since, limitAs above.

sort=activity is what makes polling useful for status and assignment changes. Under creation order, a ticket reassigned today but created weeks ago would never reach the head of the list.

{
  "tickets": [
    {
      "id": "ticket_...",
      "ticketNumber": "TKT-1042",
      "subject": "Damaged on arrival",
      "description": "The lens arrived cracked.",
      "status": "new",
      "priority": "high",
      "category": "Returns",
      "source": "ai_escalation",
      "requesterName": "Jo Rivera",
      "requesterEmail": "jo@example.com",
      "contactId": "contact_...",
      "conversationId": "conv_...",
      "conversationSummary": "Customer reports a cracked lens on order 1042.",
      "assignedToUserId": "user_...",
      "assignedToName": "Sam Patel",
      "agentId": "agent_...",
      "agentName": "Support",
      "createdAt": "2026-08-11T09:14:22.117Z",
      "lastActivityAt": "2026-08-11T10:02:41.550Z"
    }
  ]
}

POST /api/v1/tickets

curl -X POST https://tia.tdccommerce.com/api/v1/tickets \
  -H "x-api-key: $TIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "agentId": "agent_...",
        "subject": "Damaged on arrival",
        "description": "The lens arrived cracked.",
        "requesterEmail": "jo@example.com",
        "priority": "high"
      }'

agentId is required. source is recorded as api. Requires the Helpdesk module — without it the call returns 402. Returns 201.


Webhooks

Full behaviour — signing, retries, verification — is documented in Webhooks & Events.

GET /api/v1/webhooks

Lists your subscriptions and the available event names.

{
  "subscriptions": [
    {
      "id": "whs_...",
      "event": "ticket.created",
      "targetUrl": "https://example.com/hooks/tia",
      "source": "custom",
      "agentId": null,
      "status": "active",
      "failureCount": 0,
      "createdAt": "2026-08-11T09:14:22.117Z"
    }
  ],
  "availableEvents": [
    "lead.created",
    "form.submitted",
    "ticket.created",
    "ticket.assigned",
    "ticket.status_changed",
    "ticket.replied"
  ]
}

POST /api/v1/webhooks

curl -X POST https://tia.tdccommerce.com/api/v1/webhooks \
  -H "x-api-key: $TIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "event": "ticket.created", "targetUrl": "https://example.com/hooks/tia" }'
FieldNotes
eventRequired. One of the names above.
targetUrlRequired. HTTPS, no credentials, public host.
agentIdOptional. Omit for org-wide.
sourcecustom (default) or zapier.

Returns 201 with a secret (whsec_…) for custom subscriptions — store it, it's how you verify deliveries. Zapier subscriptions get no secret; the hook URL is itself the credential.

Re-subscribing the same URL to the same event is idempotent. It reactivates and resets the failure count rather than creating a duplicate that would double-deliver every future event.

Limit: 100 subscriptions per organization (429 beyond that).

DELETE /api/v1/webhooks

Pass either ?id= or both ?targetUrl= and ?event=.

{ "deleted": 1 }

Idempotent — deleting something already gone returns { "deleted": 0 } with a 200, not an error.


Building a reliable poller

If you'd rather poll than receive webhooks:

  1. Store the newest createdAt (or lastActivityAt) you've processed.
  2. Request with ?since=<that value>&limit=100.
  3. Process newest-first and de-duplicate on id.
  4. Advance your cursor only after the batch is safely handled.

Webhooks are lower-latency and lower-cost. Polling is the right choice when your receiver can't be publicly reachable.

Next steps

  • Webhooks & Events — signatures, retries, and the delivery log.
  • Zapier — the no-code path over these same endpoints.
  • Agents API — agents, sources, tools, and secrets.