- Connect Zapier — for teams who want to use Keloa in their Zaps.
- API reference — the OAuth 2 flow, the endpoints, the REST hooks and the payloads the integration uses.
The API on this page exists for the Keloa Zapier integration. Only Zapier can get an access token for it: the OAuth client is Zapier, and Keloa only redirects to Zapier’s own OAuth return address. To call Keloa from your own code, use Webhooks and the Custom HTTP tool.
Connect Zapier
Who can connect
- Role. Only workspace owners and admins can connect Keloa to Zapier. See Members & roles.
- Plan. Zapier works on every plan. It uses one integration slot per workspace, however many teammates connect it: Starter includes 1 slot, Growth 5, Business 15 and Scale unlimited. See Plans.
- Per-plan actions. Add Internal Note needs Growth or higher, because internal notes do. Keloa only sends email from a workspace with a verified email domain, which needs Growth or higher. Without one, Create Conversation and replies on email conversations are kept in the conversation but not sent.
Set it up
1
Open Keloa on Zapier
In Keloa, open Integrations in the sidebar, select the Zapier tile and click Open Keloa on Zapier. Or search for Keloa when you add a step to a Zap.
2
3
Allow access to your workspace
Sign in to Keloa. The consent page shows the workspace you have open and what Zapier may do there. Click Allow access to your workspace. If you’re an owner or admin of other workspaces, the page offers to connect one of those instead.
4
Build your Zap
Zapier labels the connection with the workspace name and your email address. Zaps act as you: replies and notes show your name, and the conversations you reply to are assigned to you, as if you had done it in the inbox.
What you can do in a Zap
Triggers
All triggers are instant (REST hooks). See Events for the data each one sends.Actions
Searches
Zap commands
A Zap command lets a teammate start a Zap from a conversation. Turn on a Zap with the New Zap Command trigger and give it a command name, for examplerefund. Then, in a conversation:
- Type
/zapin the composer and pick a command, or - click the Zapier button next to the composer (Run a Zap).
12.50 damaged. Keloa sends the conversation, its contact, the teammate, the command and the details to every Zap that listens for that command. The command never reaches the contact. Keloa adds an internal note to the conversation that says who ran which command.
- A command name uses lowercase letters, digits, dashes and underscores, starts with a letter or digit and is at most 48 characters long. A leading
/is dropped and uppercase is lowered. - A Zap with the trigger’s Command filter left empty listens for every command. Then teammates can type any command name.
- Every teammate except viewers can run Zap commands.
- If no Zap listens for a command, Keloa refuses to run it.
See and remove connections
Open Integrations → Zapier in Keloa. It lists each teammate who connected Zapier, when they connected, when the connection was last used, how many live Zaps it has, and the Zap commands Zaps listen for. Owners and admins can Disconnect a connection: its Zaps stop receiving Keloa events and can no longer act in the workspace. When the last connection is gone, Zapier frees its integration slot. A connection also ends on its own when the teammate who made it leaves the workspace. When they’re no longer an owner or admin, the connection stops working: API calls are refused right away, and the connection ends when Zapier next refreshes its access token or delivers an event.API reference
Basics
Every API call runs as the teammate who connected, in the workspace they approved, whatever workspace that teammate has open in the app. Records of other workspaces don’t exist for the call: their IDs answer
404, and searches never return them.
Optional fields that are missing, null or an empty string are ignored: an update only changes the fields you send with a value.
OAuth 2.0
Keloa is an OAuth 2.0 authorization server (RFC 6749) with PKCE (RFC 7636) and token revocation (RFC 7009).1. Authorize
Zapier sends the teammate’s browser to the consent page:
The teammate signs in to Keloa if needed, then sees the consent page for the workspace they have open. When they approve, Keloa redirects to the
redirect_uri:
2. Exchange the code for tokens
200 OK
Cache-Control: no-store. A code is valid once. If a used code is presented again, Keloa refuses it and also revokes the connection that code created, because someone else may hold it.
3. Refresh the access token
When an API call answers401, refresh the access token:
200 OK
invalid_grant and Keloa revokes the connection: reconnect with an owner or admin account.
4. Revoke a token
200 OK
token can be the access token or the refresh token: either one ends the whole connection. Its tokens stop working, its REST hooks are deleted, and when it was the workspace’s last Zapier connection, the integration slot is freed. An unknown or already revoked token also answers 200.
Token endpoint errors
Errors from/oauth/token and /oauth/revoke use the OAuth 2.0 error format:
400 Bad Request
Authentication
Send the access token as a bearer token on every API call:Rate limits
Over the limit, Keloa answers
429 with a Retry-After header (seconds) and {"message": "Too Many Attempts."}. API responses also carry X-RateLimit-Limit and X-RateLimit-Remaining.
Errors
The API always answers JSON, whateverAccept header you send. Every error body has a message. Validation errors (422) also list the fields:
422 Unprocessable Content
Objects
Every endpoint and every event uses the same object shapes, so a field you map in a Zap is the same in test and live runs.Conversation
Contact
Teammate
Message
Used by themessage.received event:
Endpoints
All paths are relative tohttps://app.keloa.ai/api/zapier/v1.
GET /me
Who connected, and to which workspace. Zapier uses it to test the connection and labels the connection<workspace_name> (<email>).
200 OK
id is the connection’s ID.
GET /users
The workspace’s teammates who can take conversations (owners, admins and agents; viewers are left out), sorted by name.200 OK
GET /teams
The workspace’s teams, sorted by name.200 OK
POST /hooks
Subscribes a REST hook: Keloa POSTs each matching event totarget_url until the hook is deleted. Zapier calls this when a Zap with a Keloa trigger is turned on.
A filter that doesn’t belong to the event is ignored. An empty filter matches everything.
201 Created
422): a target_url that isn’t a Zapier hook URL, an unknown event, an invalid command, or a workspace that already has 500 live hooks.
DELETE /hooks/{id}
Unsubscribes a REST hook. Zapier calls this when the Zap is turned off or deleted. It’s idempotent: a hook that’s already gone (or belongs to another workspace) answers the same, and nothing is deleted outside the connected workspace.200 OK
GET /triggers/{event}/samples
Up to 3 recent real events of the workspace, newest first, in exactly the payload shape of the event. Zapier uses them to test a trigger and to show sample data in the Zap editor. An empty workspace returns[].
Samples have a stable
id per record (for example conversation.created:<conversation id>), so loading samples twice shows the same records. In samples, previous_status and previous_assignee are always null.
200 OK
GET /contacts/search
Contacts with this email address or phone number, newest first, at most 10. With both, a contact must match both.
Without
email and phone, the answer is [].
200 OK
POST /contacts
Creates a contact, with the same rules as the contact card in Keloa. When the email address’s domain belongs to one of your companies, Keloa links the contact to it.201 Created with the new Contact:
422): none of name, email or phone (Enter a name, an email address or a phone number.), an invalid value, or an email address another contact already has (This address already belongs to <name>., in errors.email). Use Find Contact first, or Zapier’s Find or Create Contact, to avoid duplicates.
PATCH /contacts/{id}
Updates a contact. Send only the fields to change; fields you leave out or send empty keep their value.200 OK with the updated Contact. An unknown ID answers 404.
GET /conversations/search
Withid, that conversation. Otherwise the newest conversations (at most 10) of the contacts with contact_email, optionally in one status. Conversations merged into another one are never returned.
Without
id and contact_email, the answer is [].
200 OK
POST /conversations
Emails a contact and starts a new email conversation, like New message in the inbox. Keloa sends it from the workspace’s default mailbox, creates the contact when the address is new, and assigns the conversation to the teammate who connected. The AI agent stays out of the conversation, also when the contact answers.201 Created with the new Conversation:
When the workspace has no verified email domain, or the email can’t be sent, Keloa still creates the conversation and keeps the message in it, but the contact doesn’t receive it. The response doesn’t say so; your team sees why in the inbox.
PATCH /conversations/{id}
Changes the status, priority, assignee or team of a conversation. Send at least one field; fields you leave out or send empty stay as they are. Each change goes through the same rules as in the inbox: the timeline, SLA clocks and notifications follow.200 OK with the updated Conversation.
Errors: 404 for an unknown conversation. 422 when no field is given, for a teammate who isn’t in the workspace or is a viewer, for an unknown team, for a status change that isn’t possible, and for a conversation that was merged into another one.
POST /conversations/{id}/replies
Replies to the contact on the conversation’s own channel (email, WhatsApp, Instagram, Messenger, Telegram, Slack or the web widget), as the teammate who connected. It’s sent right away, without the inbox’s undo window. Like a reply from the inbox, it assigns an unassigned conversation to that teammate (when the workspace assigns conversations on reply), and the AI agent stops answering in it.201 Created
Errors:
404 for an unknown conversation. 422 for an empty content or a conversation that was merged into another one.
POST /conversations/{id}/notes
Adds an internal note as the teammate who connected. The contact never sees it. The assignee and teammates mentioned in it are notified, as for a note from the inbox.201 Created
402 on a plan without internal notes (Starter). 404 for an unknown conversation. 422 for an empty content or a conversation that was merged into another one.
POST /conversations/{id}/tags
Adds tags to a conversation. Tags it already has (compared case-insensitively) are skipped, and its other tags stay.200 OK with the updated Conversation. Adding a tag also fires the conversation.tagged event.
Errors: 404 for an unknown conversation. 422 without tags, with more than 20, or for a conversation that was merged into another one.
REST hooks
Keloa triggers are REST hooks. When a Zap with a Keloa trigger is turned on, Zapier subscribes a hook; when it’s turned off, Zapier unsubscribes it. In between, Keloa POSTs every matching event to the hook’starget_url.
Delivery
- Body. One event object, see Events.
X-Keloa-Deliveryis the event’sid; retries of the event reuse it. - Where. Only
httpsURLs onhooks.zapier.com. Keloa doesn’t follow redirects. - When. Events go out from a queue right after the change is saved, one delivery per hook. Conversations from testing an AI agent never fire events. Deliveries can arrive out of order, for example after a retry.
- Timeouts. 5 seconds to connect, 15 seconds in total.
Retries and removal
Keloa also deletes a hook when:
hooks.zapier.comanswers 50 deliveries in a row with an error status (a success resets the count);- its connection is revoked or disconnected (all of that connection’s hooks go);
- the teammate who connected is no longer an owner or admin, or has left the workspace (the connection is revoked at the next delivery).
Events
Every event is a JSON object with the same envelope, followed by the event’s subjects:conversation.created
conversation.created
A conversation is created on any channel: a contact starts one, or a teammate or a Zap starts an email.
conversation.status_changed
conversation.status_changed
A conversation’s status changes, by a teammate, the AI agent, a flow, a Zap or automatically.
previous_status is the status before the change. A hook with a status filter gets only changes to that status.conversation.assigned
conversation.assigned
A conversation is assigned to a teammate (also a re-assignment). Unassigning doesn’t fire it.
previous_assignee is the teammate who had it before, or null.conversation.tagged
conversation.tagged
One or more tags are added to a conversation.
added_tags lists only the new tags. A hook with a tag filter gets the event only when that tag is among added_tags.conversation.rated
conversation.rated
A contact rates a conversation in the satisfaction survey.
rating.score is 1 to 5; rating.comment is the contact’s comment or null.message.received
message.received
A contact writes on any channel, in a new or an existing conversation. Replies by teammates or the AI agent and internal notes don’t fire it.
contact.created
contact.created
A contact is created: by a message from someone new, by a teammate or by a Zap.
operator.command
operator.command
A teammate runs a Zap command on a conversation.
command is the lowercase command name, arguments the details the teammate typed (an empty string when none), and operator the teammate who ran it. A hook with a command filter gets only that command.Related
Webhooks
Signed POSTs to your own endpoint on conversation events.
Members & roles
Who’s an owner or admin in your workspace.
Plans
Integration slots and internal notes per plan.
Custom HTTP tool
Let your AI agent call your own endpoints.