Skip to navigation

API conventions

Pagination

List endpoints take offset and limit query parameters and return the page in an envelope with the total number of matching items:

curl "https://api.hams.ai/outbound-batch/batches?status=ACTIVE&offset=0&limit=100" \
-H "X-Secret-Key: $HAMS_SECRET_KEY"
{ "success": true, "data": [ … ], "count": 100, "total": 342, "limit": 100, "offset": 0 }
Endpointlimit default
GET /outbound-batch/batches100
GET /outbound-batch/batches/{batch_id}/leads100

offset defaults to 0. Iterate by adding the page size to offset until it reaches total.

Errors

Every error is a JSON object with a human-readable detail and a stable error_code you can branch on:

{ "detail": "Agent not found", "error_code": "AGENT_NOT_FOUND" }
StatusMeaning
400The request is well-formed but cannot be carried out, for example an extension with no outbound agent
401No valid key in the request
403The credential is valid but not allowed to do this
404The resource does not exist in this entity
422Validation failed; detail is a list of { "loc", "msg", "type" } entries naming each field
429Rate limited; wait for the number of seconds in the Retry-After header
500Something went wrong on our side; retry with backoff

error_code is specific where it helps (AGENT_NOT_FOUND, CALL_NOT_FOUND, CHAT_ALREADY_ENDED, KEY_SCOPE_NOT_ALLOWED, RATE_LIMITED, …) and falls back to a generic code for the status otherwise. Validation messages are sanitised: they name the field and the rule, never an internal provider.

Rate limits

Limits are counted in a sliding window and answered with 429 and Retry-After:

ScopeEndpointsLimit
Per public key/v1/ai-agents/{agent_id}/chat/init, …/stream, …/end, …/rate20 requests per minute each
Per entity, public traffic…/chat/init and …/chat/stream under /v1/500 requests per minute each
Per entity, authenticated traffic/ai-agents/{agent_id}/chat/init and …/stream500 requests per minute each
Per entity, secret keysAll secret-key requestsA per-minute allowance set on the entity

The per-key limits are keyed on the public key, so two pages using the same key share them.

Caching

GET /v1/ai-agents/{agent_id}/widget-config returns an ETag and Cache-Control headers. Send the tag back in If-None-Match to get 304 Not Modified while the configuration is unchanged, so a widget can check for changes on every page load without re-downloading it.

CORS

KeyAllowed origins
Public keyAny origin; the key’s allowed domains decide whether the request is served
Secret keyNone; call from a server

Timestamps and identifiers

Timestamps such as created, updated and scheduled_date are ISO 8601 in UTC. Identifiers are opaque strings; do not parse them.