Developers

A REST API, a remote MCP server and signed webhooks. One key opens all three.

Everything the panel does, your own software can do. You create an API key in the panel, exchange it for a short-lived token, and call the same endpoints the panel calls. The remote MCP server puts the same capabilities in front of an AI client such as Claude or ChatGPT, and webhooks push events to you instead of you polling for them.

Which plan. AI connectors start on the Reception plan (read-only); making changes needs Front Desk, and messaging your customers needs Command. The full REST API comes with the Front Desk plan and above; customer messages, campaign sends and new webhooks through an API key need Command. A request your plan does not cover is refused with 403 and reason: plan_upgrade_required, naming the plan that includes it.

Quick start

1. Create a key. Sign in to the panel and open Settings → Integrations → API keys. Pick full management or read-only, give the key a label, and copy the value. It is shown once and never again — we store only a hash of it.

2. Exchange it for a token. The long-lived key never travels on normal requests. You trade it for a JSON Web Token that expires within the hour, so a stolen token dies quickly.

curl -X POST https://api.officialaiagent.com/v1/auth/token \
  -H "Authorization: Bearer <YOUR_KEY>"   # -> {"access_token": "<JWT>"}

curl https://api.officialaiagent.com/v1/calls \
  -H "Authorization: Bearer <JWT>"

3. Call the endpoints. Every later request carries Authorization: Bearer <JWT>. When the token expires you get a 401: refresh once and retry. A second 401 is a real permission error and should be surfaced to a human, not retried in a loop.

The base URL for this environment is https://api.officialaiagent.com. Every example on this page is generated from it, so nothing here points at a host that does not exist.

MCP setup

The remote MCP server lives at https://api.officialaiagent.com/mcp and speaks the streamable HTTP transport. Claude and ChatGPT connectors sign in with OAuth 2.1: the business owner approves the business and the access level on an approval screen and can disconnect the app later in Settings. Other clients use the same API key, sent as a bearer token. Below is the configuration for four common clients.

Claude Desktop

claude_desktop_config.json

{
  "mcpServers": {
    "fideliscall": {
      "type": "http",
      "url": "https://api.officialaiagent.com/mcp",
      "headers": {
        "Authorization": "Bearer <YOUR_KEY>"
      }
    }
  }
}

Claude Code

Terminal

claude mcp add --transport http fideliscall \
  https://api.officialaiagent.com/mcp \
  --header "Authorization: Bearer <YOUR_KEY>"

ChatGPT

Settings → Connectors → Add custom connector

MCP server URL: https://api.officialaiagent.com/mcp
Authentication: OAuth

Cursor

~/.cursor/mcp.json

{
  "mcpServers": {
    "fideliscall": {
      "type": "http",
      "url": "https://api.officialaiagent.com/mcp",
      "headers": {
        "Authorization": "Bearer <YOUR_KEY>"
      }
    }
  }
}

Replace <YOUR_KEY> with the key you created. A read-only key can call the read tools and will get a 403 on the write ones — that is the intended behaviour, not a bug.

Tools

Each tool needs one scope. If your key does not carry it, the call is refused before anything is written.

MCP tools and the scope each one requires
ToolScopeWhat it is
get_overviewconfig:readRead only
get_manager_briefcalls:readRead only
analyze_callcalls:writeWrites to your business
list_servicesconfig:readRead only
list_modelsconfig:readRead only
list_closuresconfig:readRead only
list_recent_callscalls:readRead only
update_settingconfig:writeWrites to your business
add_closureconfig:writeWrites to your business
remove_closureconfig:writeWrites to your business
add_serviceconfig:writeWrites to your business
update_serviceconfig:writeWrites to your business
save_knowledgekb:write (also accepts config:write during the migration window)Writes to your business
list_bookingscalls:readRead only
list_open_slotscalls:readRead only
move_bookingcalls:writeWrites to your business
request_capabilityconfig:writeWrites to your business
list_capability_requestsconfig:readRead only
get_capability_requestconfig:readRead only
submit_capability_feedbackconfig:writeWrites to your business
list_team_daycalls:readRead only
list_tasksconfig:readRead only
complete_taskconfig:writeWrites to your business
get_attentioncalls:readRead only
get_usagecalls:readRead only
get_setup_gapsconfig:readRead only
list_action_plansconfig:readRead only
get_revenuecalls:readRead only
get_occupancycalls:readRead only
get_call_qualitycalls:readRead only
get_human_requestscalls:readRead only
get_customer_rhythmcalls:readRead only
list_staff_agendacalls:readRead only
list_findingscalls:readRead only
list_webhooksconfig:readRead only
dismiss_findingcalls:writeWrites to your business
draft_action_planconfig:writeWrites to your business
add_staffconfig:writeWrites to your business
update_staffconfig:writeWrites to your business
assign_taskconfig:writeWrites to your business
create_webhookconfig:writeWrites to your business
get_bookingcalls:readRead only
create_bookingcalls:writeWrites to your business
cancel_bookingcalls:writeWrites to your business
set_booking_outcomecalls:writeWrites to your business
revert_booking_outcomecalls:writeWrites to your business
list_no_showscalls:readRead only
record_no_show_recoverycalls:writeWrites to your business
list_customerscalls:readRead only
get_customercalls:readRead only
block_callerconfig:writeWrites to your business
unblock_callerconfig:writeWrites to your business
get_insightscalls:readRead only
get_monthly_analyticscalls:readRead only
list_staffconfig:readRead only
list_kb_documentskb:read (also accepts config:read during the migration window)Read only
delete_kb_documentkb:write (also accepts config:write during the migration window)Writes to your business
list_persona_draftsconfig:readRead only
approve_persona_draftconfig:writeWrites to your business
reject_persona_draftconfig:writeWrites to your business
get_findingcalls:readRead only
get_impactcalls:readRead only
ack_findingcalls:writeWrites to your business
reopen_findingcalls:writeWrites to your business
list_action_itemscalls:readRead only
complete_action_itemcalls:writeWrites to your business
revert_action_plan_draftconfig:writeWrites to your business
set_brief_scheduleconfig:writeWrites to your business
set_notification_prefsconfig:writeWrites to your business
list_time_offconfig:readRead only
add_time_offconfig:writeWrites to your business
remove_time_offconfig:writeWrites to your business
get_staff_utilizationcalls:readRead only
get_business_reportcalls:readRead only
get_business_overviewconfig:readRead only
get_usage_and_budgetconfig:readRead only
list_connected_appsconfig:readRead only
service_performanceconfig:readRead only
list_unresolved_bookingscalls:readRead only
list_blocked_callerscalls:readRead only
get_kb_documentkb:read (also accepts config:read during the migration window)Read only
close_daycalls:writeWrites to your business
send_customer_messageconfig:writeWrites to your business
record_email_opt_outconfig:writeWrites to your business
bulk_update_bookingscalls:writeWrites to your business
list_shift_overridesconfig:readRead only
add_shift_overrideconfig:writeWrites to your business
remove_shift_overrideconfig:writeWrites to your business
list_campaign_segmentscalls:readRead only
preview_campaigncalls:readRead only
campaign_resultscalls:readRead only
draft_campaignconfig:writeWrites to your business
generate_campaign_posterconfig:writeWrites to your business
send_campaignconfig:writeWrites to your business
list_campaign_occasionscalls:readRead only
set_customer_birthdayconfig:writeWrites to your business

Scopes

A key carries a set of scopes. "Full management" asks the server to pick the whole self-service set, so a key created today keeps working when we add a capability. "Read only" is the narrow set marked below.

Scopes available to a key created from the panel
ScopeRead-only keyOpens
config:readyesAssistant settings, services, closures, knowledge map
config:writenoWrite settings, services and closures
calls:readyesCalls and chats: list, detail, transcript, callers
calls:writenoBookings and action items
kb:readyesKnowledge base documents: list, content, preview
kb:writenoWrite to and delete from the knowledge base
s2s:mintnever issuedThe voice worker's own short-lived identity

s2s:mint is listed for completeness and is never issued from the panel. It is the voice worker's own identity and is minted server-side with a lifetime measured in seconds.

Rate limits

Limited endpoints carry two ceilings. The narrow one is per key or user; if that passes, the wider per-business one applies. The order is deliberate: a key over its own ceiling should not eat the shared budget on its way to being refused.

Per-minute rate limit lanes
LanePer businessPer key or user
Reads (list, detail)300 / min120 / min
Writes (POST, PATCH, DELETE)120 / min60 / min
Analytics (/v1/analytics*)60 / min30 / min

Endpoints that spend money or write to a ledger have their own hourly bucket on top of the per-minute lane:

Hourly buckets on spending endpoints
EndpointPer businessPer key or user
POST /v1/kb/ingest/commit/*30 / hour—
POST /v1/manager/diagnostics/calls/{id}60 / hour30 / hour
POST /v1/manager/action-plans/{id}/execute30 / hour15 / hour
POST /v1/api-keys10 / hour—
Not every endpoint is limited today. Roughly half of the /v1 surface has no server-side ceiling — /v1/config included. Write your client as if it were limited anyway: implement your own back-off rather than trusting us to stop you.

A rate-limit refusal looks like this:

HTTP/1.1 429 Too Many Requests
Retry-After: 60

{"error": {"code": "rate_limited", "reason": "principal_window"}}

Branch on reason, never on the human-readable message. principal_window means your key is over its ceiling — narrow its work or use a second key. tenant_window means the whole business is over the shared ceiling — run fewer things at once.

Retry-After is present only on rate-limit 429s. There is a second kind of 429: quota_exceeded, which means a plan allowance such as concurrent calls is used up. That response has no Retry-After, because when it clears depends on how long a live call runs, not on a fixed window. Read error.code to tell the two apart and apply your own back-off when the header is missing.

Error codes

Every error uses the same envelope: {"error": {"code": "...", "message": "..."}}. Some endpoints add a narrower reason. Branch on code and reason; treat message as text for a human.

Error codes and the HTTP status each maps to
CodeHTTPWhat it means
unauthenticated401No valid key or token in the Authorization header.
forbidden403Authenticated, but the key lacks the scope or role.
tenant_mismatch403That resource belongs to a different business account.
validation422The request parameters were rejected.
not_found404The resource does not exist.
conflict409The resource changed under you. Reload and retry.
rate_limited429A rate window is full. Retry-After is present.
quota_exceeded429A plan quota is exhausted. Retry-After is NOT present.
integration_unavailable503A connected integration is not reachable.
integration_timeout504A connected integration did not answer in time.
integration_rejected502A connected integration refused the request.
provider_unavailable503The voice provider is unavailable.
provider_session_lost503The voice session was lost. Start a new one.
internal500Something failed on our side.

One exception worth knowing before you write a sign-up flow: POST /v1/signup still answers a rate-limit refusal with a plain {"detail": "too_many_attempts"} body. Do not expect error.code there.

Webhooks

Instead of polling, register an HTTPS endpoint and we call it when something happens. You add endpoints in Settings → Integrations → Webhooks, or over the API.

The event catalogue lives on the server

Event names change as the product grows, so this page does not print a list that would quietly go stale. Ask the API for the current one:

curl https://api.officialaiagent.com/v1/webhooks/events \
  -H "Authorization: Bearer <JWT>"

The panel builds its event picker from exactly this response, so what you can subscribe to over the API and what you can tick in the panel can never disagree.

Verifying the signature

Every delivery carries an X-Fc-Signature header in the form t=<unix seconds>,v1=<hex>. The v1 value is an HMAC-SHA256 over <t>.<raw body>, keyed with the signing secret you were shown once when the endpoint was created.

import hashlib, hmac, time

SECRET = "whsec_..."          # shown once, when you create the endpoint
TOLERANCE_S = 300             # reject anything older than five minutes

def verify(raw_body: bytes, header: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    ts, got = parts.get("t", ""), parts.get("v1", "")
    if not ts.isdigit() or abs(time.time() - int(ts)) > TOLERANCE_S:
        return False                                   # replay window
    signed = ts.encode() + b"." + raw_body             # RAW body, never re-serialised
    want = hmac.new(SECRET.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(want, got)              # constant time

Three details matter. Sign the raw body — re-serialising the JSON changes the bytes and the signature will not match. Compare in constant time. And reject an old timestamp, so a captured delivery cannot be replayed at you later.

Retries and automatic disabling

A delivery that fails is retried with a growing delay. After enough consecutive failures we stop calling the endpoint and mark it disabled, with the reason visible in the panel. Answer quickly with a 2xx and do the real work afterwards; a slow handler looks the same to us as a broken one.

Full API reference

Every endpoint, parameter and response shape is published as an OpenAPI document. Browse the reference, or fetch the raw document:

curl https://officialaiagent.com/api/openapi.json

Getting help

Something missing, or an endpoint behaving differently from this page? Write to support@officialaiagent.com and tell us what you were calling — a person reads it. Business owners can also leave a request from Settings → Integrations, which lands in the same place.