API reference
Every endpoint and webhook event in the Personify API, with the exact fields each one sends.
What this is for. Personify can send your new leads and finished conversations to other tools as they happen. If you connect through the Personify app in Zapier, you do not need this page. It is here for the developer setting that up for you, and for anyone building their own connection.
The API does two things. It lets another tool subscribe to events from one of your clones, and it returns a few recent real records so fields can be mapped before the next one arrives. It cannot change your clones, their settings or their knowledge.
- A key belongs to one workspace. Everything the API returns or creates is in that workspace.
- A subscription watches one clone and one event. To watch two clones, make two subscriptions.
- There are two events:
lead.createdandconversation.ended. - Bodies are JSON in both directions. Send
Content-Type: application/jsonwith a request body. - IDs are UUIDs. Timestamps are ISO 8601, in UTC.
https://api.personify.fyi/functions/v1/zapierAuthentication
Create a key on the Integrations page of your dashboard: open Zapier, then press Create key under Your key. Keys start with pk_.
The key is shown once. Personify keeps only a hash of it, so a lost key cannot be looked up. Create a new one, and press Revoke on the old one. A revoked key stops working straight away.
Send the key on every request in the X-API-KEY header.
Header
X-API-KEYstringRequiredYour API key.
Authorization: Bearer <key>is accepted as well.
When the key is refused
The key is missing, wrong or revoked.
"error": "An API key is required""error": "That API key is not valid"
curl https://api.personify.fyi/functions/v1/zapier/me \
-H "X-API-KEY: pk_your_key_here"{
"error": "An API key is required"
}Errors
An error comes back as a JSON object with an error message. The status code says what kind of problem it is.
A required field is missing, or it points at something outside your workspace.
The key is missing, wrong or revoked.
{"error": "Unknown route"}. The path or method does not exist.
The subscription limit is reached.
Something failed on our side.
{
"error": "Unknown route"
}Limits
- Subscriptions per workspace
- 25, counting every subscription on every clone
- Deliveries per subscription
- 20 a minute
- Delivery attempts
- 5, spread over about two and a half hours
- Response timeout
- 10 seconds
- Transcript
- 200 messages or 65,536 characters, and 4,000 characters per message
- Samples
- 20 leads, 10 conversations
Test the connection
https://api.personify.fyi/functions/v1/zapier/meChecks that a key works and says which workspace it belongs to. Zapier calls this to test the connection and to name it in your Zapier account.
Request
Header parameters
X-API-KEYstringRequiredYour API key.
Authorization: Bearer <key>is accepted as well.
Responses
The key works.
iduuidThe workspace the key belongs to.
namestringThat workspace's name.
The key is missing, wrong or revoked.
"error": "An API key is required""error": "That API key is not valid"
curl https://api.personify.fyi/functions/v1/zapier/me \
-H "X-API-KEY: pk_your_key_here"{
"id": "5d0c7a2e-3f41-4b8e-9c6a-1e2f3a4b5c6d",
"name": "Example Coaching"
}List clones
https://api.personify.fyi/functions/v1/zapier/clonesLists the clones in the workspace, oldest first. Use an id from here as the cloneId in the calls below.
Request
Header parameters
X-API-KEYstringRequiredYour API key.
Authorization: Bearer <key>is accepted as well.
Responses
An array of clones. A workspace with no clones returns [].
iduuidThe clone.
namestringThe clone's name.
The key is missing, wrong or revoked.
"error": "An API key is required""error": "That API key is not valid"
curl https://api.personify.fyi/functions/v1/zapier/clones \
-H "X-API-KEY: pk_your_key_here"[
{
"id": "c1e92bf4-6b1b-4a1a-8ca5-762c28f90c97",
"name": "Nutrition Coach"
}
]Subscribe to an event
https://api.personify.fyi/functions/v1/zapier/subscribeStarts sending one event from one clone to a URL. Zapier calls this when a Zap is switched on.
Request
Header parameters
X-API-KEYstringRequiredYour API key.
Authorization: Bearer <key>is accepted as well.Content-Typestringapplication/json
Body parameters
targetUrlstringRequiredThe HTTPS URL that should receive the event.
hookUrlis accepted as another name for it.eventstringRequiredlead.createdorconversation.ended.cloneIduuidRequiredA clone
idfrom GET /clones.
Allowed destinations. The URL must start with https:// and must not contain a port or a username and password. Its host must be hooks.zapier.com, make.com or app.n8n.cloud, or a subdomain of one of them. Any other URL is refused.
Subscribing twice. Subscribing the same targetUrl to the same clone again does not create a second subscription. You get 201 with the id of the one that already exists, and it keeps the event it was created with.
Responses
The subscription exists.
iduuidThe subscription. Keep it: it is what POST /unsubscribe needs.
A field is missing or refused.
"error": "targetUrl is required""error": "Unknown event""error": "cloneId is required""error": "That clone is not in your workspace""error": "That URL is not an allowed destination"
The key is missing, wrong or revoked.
"error": "An API key is required""error": "That API key is not valid"
The workspace already has 25 subscriptions. Remove a subscription you no longer use, then try again.
errorstringYou have reached 25 live subscriptionsdetailstringTurn off a Zap you no longer use, then try again.
curl -X POST https://api.personify.fyi/functions/v1/zapier/subscribe \
-H "X-API-KEY: pk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"targetUrl": "https://hooks.zapier.com/hooks/standard/123/abc/",
"event": "lead.created",
"cloneId": "c1e92bf4-6b1b-4a1a-8ca5-762c28f90c97"
}'{
"id": "7b3e6f10-2c4d-4e8a-b1f2-9d0c3a5e7f61"
}Unsubscribe
https://api.personify.fyi/functions/v1/zapier/unsubscribeStops a subscription. Zapier calls this when a Zap is switched off or deleted. DELETE /unsubscribe does the same thing.
Request
Header parameters
X-API-KEYstringRequiredYour API key.
Authorization: Bearer <key>is accepted as well.Content-Typestringapplication/json
Body parameters
iduuidRequiredThe
idfrom POST /subscribe. It can be sent as a query parameter instead,/unsubscribe?id=....
Responses
Also 200 when no subscription in your workspace has that id, so calling it twice is harmless.
okbooleanAlways
true.
No id was sent.
"error": "id is required"
The key is missing, wrong or revoked.
"error": "An API key is required""error": "That API key is not valid"
curl -X POST https://api.personify.fyi/functions/v1/zapier/unsubscribe \
-H "X-API-KEY: pk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"id": "7b3e6f10-2c4d-4e8a-b1f2-9d0c3a5e7f61"
}'{
"ok": true
}Sample leads
https://api.personify.fyi/functions/v1/zapier/samples/leadsReturns up to 20 of the clone's most recent leads, newest first, in the shape of the lead.created event. Zapier uses these to show real fields while you build a Zap.
Request
Header parameters
X-API-KEYstringRequiredYour API key.
Authorization: Bearer <key>is accepted as well.
Query parameters
cloneIduuidRequiredA clone
idfrom GET /clones.
Responses
An array of leads. Each has exactly the fields of the lead.created data object, plus id. A clone with no leads returns [].
Show 13 fields
iduuidThe same value as
lead_id.lead_iduuidThe lead.
clone_iduuidThe clone that captured it.
clone_namestringThat clone's name.
conversation_iduuid or nullThe conversation the lead came from.
emailstring or nullThe email address the visitor gave.
namestring or nullOnly filled in when the clone's form asks for a name.
outcomestringOne of
pending,qualified,nurture,disqualifiedorbooked. The default ispending.source_urlstring or nullThe page the lead came from.
utm_sourcestring or nullUTM source recorded with the lead.
utm_mediumstring or nullUTM medium recorded with the lead.
utm_campaignstring or nullUTM campaign recorded with the lead.
created_attimestampWhen the lead was saved.
The cloneId is missing or not a clone in your workspace.
"error": "That clone is not in your workspace"
The key is missing, wrong or revoked.
"error": "An API key is required""error": "That API key is not valid"
curl "https://api.personify.fyi/functions/v1/zapier/samples/leads?cloneId=c1e92bf4-6b1b-4a1a-8ca5-762c28f90c97" \
-H "X-API-KEY: pk_your_key_here"[
{
"id": "9383a309-e727-4bd3-998d-ef1af4bafc03",
"lead_id": "9383a309-e727-4bd3-998d-ef1af4bafc03",
"clone_id": "c1e92bf4-6b1b-4a1a-8ca5-762c28f90c97",
"clone_name": "Nutrition Coach",
"conversation_id": "b2f0a9c4-16c1-4d0e-9a1b-2c8f7d3e4a55",
"email": "visitor@example.com",
"name": null,
"outcome": "pending",
"source_url": "https://example.com/coaching",
"utm_source": "newsletter",
"utm_medium": "email",
"utm_campaign": "spring",
"created_at": "2026-09-16T11:08:36.912345+00:00"
}
]Sample conversations
https://api.personify.fyi/functions/v1/zapier/samples/conversationsReturns up to 10 of the clone's most recently finished conversations, newest first, in the shape of the conversation.ended event. Test and preview chats are left out.
Request
Header parameters
X-API-KEYstringRequiredYour API key.
Authorization: Bearer <key>is accepted as well.
Query parameters
cloneIduuidRequiredA clone
idfrom GET /clones.
Responses
An array of conversations. Each has exactly the fields of the conversation.ended data object, plus id. Sample transcripts are never shortened, so transcript_truncated is always false here.
Show 14 fields
iduuidThe same value as
conversation_id.conversation_iduuidThe conversation.
clone_iduuidThe clone that held it.
clone_namestringThat clone's name.
lead_iduuid or nullThe lead captured in this conversation, if there was one.
started_attimestampWhen the conversation started.
ended_attimestampThe time of the last message, not the time this was sent.
message_countintegerHow many messages the conversation has.
duration_secondsintegerSeconds from
started_attoended_at.summarystring or nullA one-line summary of the conversation. Null when none was written.
page_urlstring or nullThe page the conversation happened on.
referrer_urlstring or nullThe referring URL recorded for the conversation.
transcriptarray of objectsThe messages, oldest first.
Each item
rolestringuserfor the visitor andassistantfor the clone.systemandagentare also possible values.contentstringThe message, cut to its first 4,000 characters.
created_attimestampWhen the message was created.
iduuidThe message.
transcript_truncatedbooleantruewhen messages were left off the end oftranscript.
The cloneId is missing or not a clone in your workspace.
"error": "That clone is not in your workspace"
The key is missing, wrong or revoked.
"error": "An API key is required""error": "That API key is not valid"
curl "https://api.personify.fyi/functions/v1/zapier/samples/conversations?cloneId=c1e92bf4-6b1b-4a1a-8ca5-762c28f90c97" \
-H "X-API-KEY: pk_your_key_here"[
{
"id": "b2f0a9c4-16c1-4d0e-9a1b-2c8f7d3e4a55",
"conversation_id": "b2f0a9c4-16c1-4d0e-9a1b-2c8f7d3e4a55",
"clone_id": "c1e92bf4-6b1b-4a1a-8ca5-762c28f90c97",
"clone_name": "Nutrition Coach",
"lead_id": "9383a309-e727-4bd3-998d-ef1af4bafc03",
"started_at": "2026-09-16T10:41:02.118204+00:00",
"ended_at": "2026-09-16T11:12:44.503771+00:00",
"message_count": 2,
"duration_seconds": 1902,
"summary": "Asked about one-to-one coaching and left an email address.",
"page_url": "https://example.com/coaching",
"referrer_url": "https://www.google.com/",
"transcript": [
{
"role": "user",
"content": "Do you take on new clients?",
"created_at": "2026-09-16T10:41:02.118204+00:00",
"id": "1f7a2b3c-4d5e-6f70-8192-a3b4c5d6e7f8"
},
{
"role": "assistant",
"content": "I do, and here is how I usually start.",
"created_at": "2026-09-16T11:12:44.503771+00:00",
"id": "2a8b3c4d-5e6f-7081-92a3-b4c5d6e7f809"
}
],
"transcript_truncated": false
}
]Webhook events
Delivery and signing
When an event happens, Personify sends a POST to each matching subscription's URL, with this JSON body:
Body
eventstringlead.createdorconversation.ended.created_atstringWhen the delivery was created, in UTC, to the second. For example
2026-09-16T11:08:37Z.testbooleantruewhen the event came from a test rather than a real visitor, such as a chat the clone's owner held while trying it out.dataobjectThe event's own fields, listed under each event below.
Headers
Content-Typestringapplication/jsonUser-AgentstringPersonify-Webhooks/1X-Personify-EventstringThe event name, the same as
eventin the body.X-Personify-DeliveryuuidA UUID for this delivery. A retry keeps the same value, so use it to skip a delivery you have already handled.
X-Personify-Signaturestringt=<unix seconds>,v1=<signature>. See signing below.
Signing
v1 is a hex HMAC-SHA256 of <t>.<body>: the t value, a full stop, then the raw request body, keyed with the subscription's signing secret. Compute it over the body exactly as it arrived, before parsing it. The API does not return the signing secret for subscriptions it creates.
Success and retries
- Any
2xxresponse counts as delivered. Any other status, or no response within 10 seconds, is a failure. - A failed delivery is tried again, up to 5 attempts in all. The waits between attempts are about 1 minute, 5 minutes, 30 minutes and 2 hours. After the fifth failure it stops.
- Each subscription is sent at most 20 deliveries a minute. Anything over that waits for a later minute rather than being dropped.
- An event is sent once per subscription for each lead or conversation. The only repeats are retries.
POST /hooks/standard/123/abc/ HTTP/1.1
Host: hooks.zapier.com
Content-Type: application/json
User-Agent: Personify-Webhooks/1
X-Personify-Event: lead.created
X-Personify-Delivery: 3c9d1e7a-5b2f-4c8d-a6e0-7f1b2d3c4e5a
X-Personify-Signature: t=1789556917,v1=<hex HMAC-SHA256>
{"data": {...}, "test": false, "event": "lead.created", "created_at": "2026-09-16T11:08:37Z"}Webhook event
lead.created
When it fires: A visitor gives your clone their details and a lead is saved. It is sent straight away and normally arrives within a minute.
The data object
lead_iduuidThe lead.
clone_iduuidThe clone that captured it.
clone_namestringThat clone's name.
conversation_iduuid or nullThe conversation the lead came from.
emailstring or nullThe email address the visitor gave.
namestring or nullOnly filled in when the clone's form asks for a name.
outcomestringOne of
pending,qualified,nurture,disqualifiedorbooked. The default ispending.source_urlstring or nullThe page the lead came from.
utm_sourcestring or nullUTM source recorded with the lead.
utm_mediumstring or nullUTM medium recorded with the lead.
utm_campaignstring or nullUTM campaign recorded with the lead.
created_attimestampWhen the lead was saved.
{
"event": "lead.created",
"created_at": "2026-09-16T11:08:37Z",
"test": false,
"data": {
"lead_id": "9383a309-e727-4bd3-998d-ef1af4bafc03",
"clone_id": "c1e92bf4-6b1b-4a1a-8ca5-762c28f90c97",
"clone_name": "Nutrition Coach",
"conversation_id": "b2f0a9c4-16c1-4d0e-9a1b-2c8f7d3e4a55",
"email": "visitor@example.com",
"name": null,
"outcome": "pending",
"source_url": "https://example.com/coaching",
"utm_source": "newsletter",
"utm_medium": "email",
"utm_campaign": "spring",
"created_at": "2026-09-16T11:08:36.912345+00:00"
}
}Webhook event
conversation.ended
When it fires: A conversation counts as finished 30 minutes after its last message. So this arrives roughly half an hour after the visitor stops, not while they are still chatting.
Testing it? Send a message, then expect to wait about half an hour. Nothing arriving in the first few minutes does not mean it is broken.
The data object
conversation_iduuidThe conversation.
clone_iduuidThe clone that held it.
clone_namestringThat clone's name.
lead_iduuid or nullThe lead captured in this conversation, if there was one.
started_attimestampWhen the conversation started.
ended_attimestampThe time of the last message, not the time this was sent.
message_countintegerHow many messages the conversation has.
duration_secondsintegerSeconds from
started_attoended_at.summarystring or nullA one-line summary of the conversation. Null when none was written.
page_urlstring or nullThe page the conversation happened on.
referrer_urlstring or nullThe referring URL recorded for the conversation.
transcriptarray of objectsThe messages, oldest first.
Each item
rolestringuserfor the visitor andassistantfor the clone.systemandagentare also possible values.contentstringThe message, cut to its first 4,000 characters.
created_attimestampWhen the message was created.
iduuidThe message.
transcript_truncatedbooleantruewhen messages were left off the end oftranscript.
To keep deliveries a sensible size, each message's content is cut to its first 4,000 characters, and the transcript stops at 200 messages or 65,536 characters in total, whichever comes first. transcript_truncated is true only when whole messages were left out, not when one was cut short.
{
"event": "conversation.ended",
"created_at": "2026-09-16T11:43:10Z",
"test": false,
"data": {
"conversation_id": "b2f0a9c4-16c1-4d0e-9a1b-2c8f7d3e4a55",
"clone_id": "c1e92bf4-6b1b-4a1a-8ca5-762c28f90c97",
"clone_name": "Nutrition Coach",
"lead_id": "9383a309-e727-4bd3-998d-ef1af4bafc03",
"started_at": "2026-09-16T10:41:02.118204+00:00",
"ended_at": "2026-09-16T11:12:44.503771+00:00",
"message_count": 2,
"duration_seconds": 1902,
"summary": "Asked about one-to-one coaching and left an email address.",
"page_url": "https://example.com/coaching",
"referrer_url": "https://www.google.com/",
"transcript": [
{
"role": "user",
"content": "Do you take on new clients?",
"created_at": "2026-09-16T10:41:02.118204+00:00",
"id": "1f7a2b3c-4d5e-6f70-8192-a3b4c5d6e7f8"
},
{
"role": "assistant",
"content": "I do, and here is how I usually start.",
"created_at": "2026-09-16T11:12:44.503771+00:00",
"id": "2a8b3c4d-5e6f-7081-92a3-b4c5d6e7f809"
}
],
"transcript_truncated": false
}
}Getting help
Email personify@deepquery.dev with the endpoint you called, the status you got back and the X-Personify-Delivery value if it is about a delivery. Never send your API key.