Skip to main content
The Vorel public API is a REST surface at https://app.vorel.ai/api/v1/*. It’s the same surface our @vorel/sdk TypeScript client uses, and the same surface your custom integrations should target. If you are connecting an AI client rather than writing an integration, see MCP.

Quickstart

1

Generate an API key

Sign in at app.vorel.ai, navigate to Settings → Integrations → API keys, click Create API key. Pick the scopes your integration needs: read, leads:write, appointments:write, offerings:write, conversations:write, crm:write, assistant:read. Copy the key; it’s shown only once (scrypt-hashed in storage).The key format is vapk_live_<48 hex chars> (58 chars total). See Authentication for the full lifecycle.
2

Make your first request

Returns a paginated list of your tenant’s conversations.
3

Browse the full reference

The API Reference tab in this docs site auto-renders from our live OpenAPI spec: every endpoint, every parameter, every response shape, with interactive “try it” examples per route.

What the API exposes

Conversations

Read inbound conversations across voice + chat. List, get-by-id, create (pre-create from your CRM webhook), update.

Leads

Read + write leads. Update qualification state, attach attributes, trigger handoff, mark stale.

Appointments

Read + write appointments. Schedule, reschedule, cancel, complete. Lifecycle webhooks emit on every transition.

Offerings

Read + write your catalog (properties / services / treatments / menu items, vertical-specific). Soft-delete supported.

Analytics

GET /v1/analytics/weekly-rollup: aggregate metrics for the prior N days. Point your own reporting at this.

CRM proxy

POST /v1/crm/create-record: write into your tenant’s connected CRM via Vorel’s per-tenant driver layer. Same path the agent uses.
For the complete endpoint catalog, see the API Reference tab.

Conventions

Auth

All endpoints require Authorization: Bearer <api_key>. Keys are tenant-scoped; one key only sees its own tenant’s data, enforced via Postgres RLS. Endpoints that mutate state require a write scope (leads:write, appointments:write, offerings:write, conversations:write). Endpoints that only read accept the read scope. A key without the matching scope returns a 403 forbidden envelope.

Response shapes

Successful responses are JSON. List endpoints use cursor pagination:
next_cursor and has_more are top-level fields alongside data (not nested under a pagination object). Pass next_cursor back as ?cursor=<uuid> to fetch the next page; has_more is false and next_cursor is null when you’ve reached the end. Single-resource endpoints return the resource directly.

Error envelope

Every error returns a consistent shape:
Codes: unauthorized, forbidden, rate_limited, bad_request, not_found, conflict, internal_error. The HTTP status code matches the semantic.

Idempotency

POST /v1/crm/create-record accepts an idempotency_key field in the request body. The value is forwarded into the per-tenant CRM driver so the underlying CRM can dedupe its own write. Other write endpoints don’t currently accept an idempotency key; safe-retry semantics there rely on the resource’s natural unique constraints (e.g. (tenant_id, channel, customer_identifier) for conversations).

Webhooks

The API also emits outbound webhooks to URLs you register at /(dashboard)/settings/integrations/webhooks. 19 event types spanning leads, conversations, bookings, calls, outbound attempts, SLA breaches, CSAT and catalog changes. See Webhooks for the full table. Bodies are HMAC-SHA256 signed via X-Webhook-Signature; the envelope id field doubles as the consumer’s idempotency key. See Webhooks for the full spec.

Rate limits

API requests are gated by a layered rate-limit stack. The most-restrictive applicable layer wins: The limiter is fail-open: a cache blip admits the request rather than 429-ing every caller. Hitting a limit returns 429 rate_limited with Retry-After + X-RateLimit-Limit + X-RateLimit-Remaining + X-RateLimit-Reset headers. See Rate limits for the detailed stack.

SDKs

TypeScript / JavaScript

@vorel/sdk is a full-coverage, typed client with zero runtime deps. Today the REST API plus the live OpenAPI spec are the supported integration path; a published @vorel/sdk package is on the roadmap.

OpenAPI (any language)

Live OpenAPI 3.1 spec. Import into Postman / Insomnia / Bruno, or generate a client in any language via openapi-generator / oazapfts / etc.

Common integration patterns

Call POST /v1/conversations to pre-create the conversation from your form’s customer-identifier (phone/email), then POST /v1/leads to attach the qualification data. The next time the customer calls or WhatsApps, Vorel matches them to the existing conversation by (channel, customer_identifier). No fragmentation.
Subscribe to the lead.qualified webhook event. Vorel POSTs to your URL with the lead row + the conversation’s last 10 messages. HMAC-signed; verify the signature before acting.
Build this as a trigger rule rather than in code. The booking.created event carries the scheduled start, so a rule with relative timing fires a set number of hours before the appointment. If you would rather own the send, subscribe to booking.created and call POST /v1/conversations/{id}/send on your own schedule.
GET /v1/leads?stale_for_days=3 returns qualified leads whose conversation has gone quiet for more than N days. Pair it with POST /v1/conversations/{id}/send. The filter already scopes to qualified leads, so no separate status filter is needed, and none is supported today.
GET /v1/analytics/weekly-rollup on a Monday schedule, formatted and sent wherever you want it. Vorel also ships its own daily digest if you do not need a custom format.

Status

The API is live and stable. All v1/* endpoints have stable contracts; we’ll version-bump (/v2) before any breaking change. Public status page: app.vorel.ai/status. Expect ~99.9% on the API surface (per our SLOs).