API & webhooks

Give your AI operators work from your own software, hear back when something happens, and pull every record out. Version 1.

Keys

Create a key in HQ → Developers → API keys. Each key has one or more scopes. Send it on every request:

curl https://www.getagentically.com/api/v1/me \
  -H "Authorization: Bearer aga_your_key"
ScopeLets the key
readList operators, approvals, activity, tool calls, locations, calls
operateSend a message to an operator; add or edit locations
approveApprove or reject items in the approvals inbox
adminEverything above, plus webhooks, pause / resume, full export

A key is shown once. We store only a hash of it. Revoke it in the same place.

Endpoints

Base URL: https://www.getagentically.com/api/v1 · JSON in, JSON out.

PathScopeWhat it does
GET/meanyPlan, model, paused state, this key's scopes
GET/operatorsreadOperators with status, connections, runs today, daily cap
GET/operators/:id/messagesreadRecent conversation. ?limit= up to 120
POST/operators/:id/messagesoperateGive the operator a task and wait for its reply. :id may be the id or the name. Body {"text":"…"}
GET/approvalsread?status=pending|approved|rejected|all
POST/approvals/:id/approveapproveApprove. If it is a queued tool action, it runs now and the result is returned
POST/approvals/:id/rejectapproveReject. Optional {"note":"why"} becomes a lesson for the operator
GET/activityreadActivity log. ?since=ISO&limit=&format=csv
GET/tool-callsreadEvery call an operator made to a connected tool. ?format=csv
GET/locationsreadLocations with pins, floor-plan spots and live operator status
POST/locationsoperateAdd a location. {"name","address","kind","lat","lng","status","notes"}
PATCH/locations/:idoperateChange a location (for example its status)
GET/callsreadPhone calls with the text of each turn
GET/webhooksadminList outgoing webhooks
POST/webhooksadminAdd one. {"url","events":["approval.created"],"label"}. Returns the signing secret once
DELETE/webhooks/:idadminRemove one
POST/pauseadminStop every operator
POST/resumeadminStart them again
GET/exportadminEverything in the workspace as one JSON document, without secrets

Example: hand an operator a task

curl -X POST https://www.getagentically.com/api/v1/operators/Maya/messages \
  -H "Authorization: Bearer aga_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text":"Write the listing description for 1450 Brickell Ave #2101."}'

{ "operator": "Maya", "text": "Here is a first draft…", "approvalsQueued": 1 }

The reply can take up to a minute. Anything the operator wants to send or change is not done yet — it is waiting in /approvals.

Webhooks out

Add a URL in HQ → Developers or with POST /webhooks. We POST JSON to it when one of these happens:

EventWhen
approval.createdAn operator queued something for a person to approve
approval.resolvedSomeone approved or rejected it (resolvedBy says who)
operator.repliedAn operator finished a turn (chat, schedule, API, hook or phone)
hook.receivedAn inbound hook delivered an event to an operator
call.completedA phone call ended; includes the text of each turn
workspace.paused / workspace.resumedThe pause switch changed
POST your-url
x-agentically-event: approval.created
x-agentically-delivery: evt_ab12cd34
x-agentically-signature: t=1791575000,v1=5f2c…

{ "id": "evt_ab12cd34", "type": "approval.created", "created": "2026-10-09T20:00:00.000Z",
  "data": { "id": "ap_…", "kind": "tool_call", "title": "Gmail · send message — to: …",
            "operator": { "id": "op_…", "name": "Maya" } } }

Check the signature

v1 is HMAC-SHA256 of t + "." + raw body, using your signing secret, in hex. Reject messages where t is more than five minutes old.

const crypto = require("crypto");
function verify(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map(p => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const want = crypto.createHmac("sha256", secret).update(t + "." + rawBody).digest("hex");
  return v1.length === want.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(want));
}

Answer with any 2xx within 8 seconds. A failed delivery is retried after 5 seconds and again after 30. After 20 failed events in a row the webhook is turned off; "Send test" in HQ turns it back on. URLs must be https and public.

Inbound hooks

Create one in HQ → Developers → Webhooks in and choose which operator receives it. You get a private URL:

curl -X POST https://www.getagentically.com/hooks/agh_your_private_token \
  -H "Content-Type: application/json" \
  -d '{"event":"new_lead","name":"Dana Ruiz","phone":"+13055550142","source":"Zillow","message":"Is 88 SW 7th still available?"}'

202 { "ok": true, "id": "in_…", "operator": "Leo" }

Send any JSON, up to 6,000 characters of it is passed on. We answer 202 straight away and the operator starts working. It treats the payload as information, not as orders, and follows its own standing instructions for that kind of event. Listen for operator.replied and approval.created to see what it did. Limit: 30 events a minute per hook.

Zapier & Make

No special app is needed.

Errors & limits

StatusMeaning
400Something in the request is missing or wrong. error says what
401No key, or the key is not valid
402The subscription is canceled or a payment failed
403The key does not have the scope this needs
404No such item
423The workspace is paused, or that operator hit its daily limit
429Too many requests (120 a minute per key), or the plan's daily usage is used up
502 / 503The AI model did not answer. Try again

Errors look like {"error":"code","message":"plain words"}. Questions: hello@getagentically.com.