Skip to main content
FormBharo’s API follows a consistent set of conventions for paths, pagination, and timestamps. Understanding these upfront prevents common integration mistakes.

Base path

All endpoints sit under /api/v1 on your FormBharo server. For example, to list agents on a server at , the full URL is /api/v1/agents.
Exception: POST /offer (the WebRTC signalling endpoint) is at the bare path — /offer — with no /api/v1 prefix.

Pagination

List endpoints support two query parameters for paging through results: cursor is a positional offset, not tied to a specific row’s identifier. This means live inserts that happen between two page requests can cause the same record to appear on two consecutive pages, or for a record to be skipped entirely. For most use cases this is acceptable — but avoid building deduplication logic that depends on cursor-based paging being gap-free. When the last page is reached, next_cursor in the response is null.

Two response shapes on the same list endpoint

Some list endpoints return different JSON shapes depending on whether you include pagination parameters: Without limit or cursor — the response is a plain JSON array:
With limit or cursor present — the response is a paginated envelope object:
This behaviour applies to:
  • GET /api/v1/agents
  • GET /api/v1/agents/{agent_id}/conversations
  • GET /api/v1/workspaces
GET /api/v1/conversations (the cross-agent conversation list) always returns the envelope shape — { "data": [...], "next_cursor": ... } — regardless of whether you pass pagination parameters.

Timestamps

All timestamps in FormBharo are unix seconds expressed as a fractional (floating-point) number. There are no date strings and no timezone objects.
The since and until query parameters on list endpoints filter on created_at. In FormBharo, created_at is the last-write time — it is updated whenever the record is modified, not set once at creation.
A past date range is never truly final. If someone edits an answer on a call that originally happened last week, that call’s created_at moves to the current moment and it will reappear in a fresh query for the current window. Always match incoming records by conversation_id and update your local copy rather than assuming a historical range is complete.

Transcript inclusion

GET /api/v1/agents/{agent_id}/conversations includes the full call transcript for each conversation by default. Transcripts can be large — if you only need form data and metadata, pass include_transcript=false to omit them.
When omitted, the transcript key is absent from each conversation record entirely — it is not present with a null or empty value. Check for key existence, not for a falsy value, if your code needs to distinguish “transcript omitted” from “no transcript recorded”.