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
| Format | JSON in, JSON out. |
| Errors | { "error": { "message": string, "status": number } } |
| Caching | Every response is Cache-Control: no-store. |
limit | List endpoints accept limit (default 50, max 100). |
since | ISO 8601 timestamp filter. Invalid values are ignored, not rejected — a stored bad value shouldn't wedge an otherwise-working integration. |
| Ordering | Lists are newest-first, so a poller can read the head and stop at the first id it has seen. |
Status codes
| Code | Meaning |
|---|---|
400 | Malformed request or missing required field. |
401 | Missing or invalid API key. |
402 | Your plan doesn't include this capability. |
403 | Valid key, but you're not a member of that organization. |
404 | The referenced agent doesn't exist in this organization. |
429 | Subscription 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
| Query | Notes |
|---|---|
agentId | Scope to one agent. |
email | Exact match, case-insensitive. |
since | ISO 8601. |
limit | 1–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
| Query | Notes |
|---|---|
agentId | Scope to one agent. |
formName | The form's stable key, e.g. warranty_claim. |
since, limit | As 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
| Query | Notes |
|---|---|
agentId | Scope to one agent. |
status | new, on_you, on_customer, on_hold, closed, cancelled. |
email | Requester email, exact match. |
ticketNumber | e.g. TKT-1042. |
sort=activity | Order by last activity instead of creation. |
since, limit | As 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" }'
| Field | Notes |
|---|---|
event | Required. One of the names above. |
targetUrl | Required. HTTPS, no credentials, public host. |
agentId | Optional. Omit for org-wide. |
source | custom (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:
- Store the newest
createdAt(orlastActivityAt) you've processed. - Request with
?since=<that value>&limit=100. - Process newest-first and de-duplicate on
id. - 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.