https://app.haloagents.ai/api/v1 are for integrations that act on behalf of a workspace: the Zapier app is built on them. They differ from the SDK endpoints in one way: they take an OAuth 2.0 access token, never the publishable widget key.
The REST API is the API access capability (Pro and up). A token for a workspace on another plan gets
403 upgrade_required.Authentication
Halo’s authorization server is the one the MCP connector uses. Discovery lives at/.well-known/oauth-authorization-server; the flow is authorization code with PKCE, refresh tokens rotate, and the consent screen lets the person pick the scopes they grant.
To get a token that works here, bind the grant to this API with the RFC 8707 resource parameter on both the authorize and the token request:
POST /api/mcp/oauth/register (dynamic client registration; use token_endpoint_auth_method: client_secret_post for a confidential client).
Scopes
A call the token’s scopes do not cover returns
403 insufficient_scope. Every call is also limited to what the person who granted the token can do in the dashboard.
Errors
Errors are{ "error": "<code>", "error_description": "<message>" }. 401 invalid_token carries a WWW-Authenticate challenge; 429 rate_limited carries Retry-After (300 requests per minute per token).
Endpoints
GET /api/v1/me
The workspace, the person who granted the token and the scopes it holds.
GET /api/v1/tools and POST /api/v1/tools/{name}
The tools the token may call, with their JSON Schema, and a way to run one: the body is the tool’s arguments, the response is { "tool": "<name>", "result": … }. These are the same tools the MCP connector exposes (inbox_create_ticket, inbox_reply_ticket_email, data_create_user, data_update_company, cs_create_deal, cs_move_deal_stage, create_task, cs_create_issue, workflow_enroll_records, …), run through the same validation, permission checks and audit log.
GET /api/v1/records/{type}
The newest records of one kind, in the same shape webhook deliveries carry them, with simple filters. limit is 1–50 (default 3).
Response:
{ "records": [ … ] }.
GET /api/v1/lookups/{kind}
Short { id, name } lists for pickers: members, agents, inboxes, pipelines, stages (?pipeline_id= narrows), workflows, ticket_statuses, issue_priorities. Response: { "items": [ … ] }.
Webhooks
A webhook subscription asks Halo to POST events to a URL. Subscriptions are per workspace and per token’s OAuth client: deliveries stop while that client holds no live token (the person disconnected it).GET /api/v1/webhooks/events
The catalog. Each event needs its object’s read scope to subscribe.
POST /api/v1/webhooks
201 with { "webhook": { … }, "secret": "whsec_…" }. The secret is shown once. url must be https on a public host. A workspace may hold 100 active subscriptions.
GET /api/v1/webhooks, GET /api/v1/webhooks/{id}, DELETE /api/v1/webhooks/{id}
List, read, and remove the subscriptions this token’s OAuth client registered; another client’s are never visible. DELETE answers 204, or 404 when it was already gone.
Deliveries
Each delivery is aPOST with the envelope:
id is stable across retries of the same event: deduplicate on it. Headers:
Verify by recomputing the HMAC over
<timestamp>.<raw body> with the subscription’s secret and comparing in constant time; reject timestamps older than a few minutes.
Answer 2xx within 10 seconds. A 5xx, 408, 429 or timeout is retried with backoff; any other 4xx is final for that event. After 10 consecutive failed events the subscription is switched off (disabled_reason: too_many_failures); a 410 Gone switches it off at once (receiver_gone).