Live in 10 minutes. Free forever

    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.created and conversation.ended.
    • Bodies are JSON in both directions. Send Content-Type: application/json with a request body.
    • IDs are UUIDs. Timestamps are ISO 8601, in UTC.
    Base URL
    https://api.personify.fyi/functions/v1/zapier

    Authentication

    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-KEYstringRequired

      Your API key. Authorization: Bearer <key> is accepted as well.

    When the key is refused

    401

    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"
    Response 401
    {
      "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.

    400

    A required field is missing, or it points at something outside your workspace.

    401

    The key is missing, wrong or revoked.

    404

    {"error": "Unknown route"}. The path or method does not exist.

    409

    The subscription limit is reached.

    500

    Something failed on our side.

    Error body 404
    {
      "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

    GEThttps://api.personify.fyi/functions/v1/zapier/me

    Checks 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-KEYstringRequired

      Your API key. Authorization: Bearer <key> is accepted as well.

    Responses

    200

    The key works.

    • iduuid

      The workspace the key belongs to.

    • namestring

      That workspace's name.

    401

    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"
    Response 200
    {
      "id": "5d0c7a2e-3f41-4b8e-9c6a-1e2f3a4b5c6d",
      "name": "Example Coaching"
    }

    List clones

    GEThttps://api.personify.fyi/functions/v1/zapier/clones

    Lists the clones in the workspace, oldest first. Use an id from here as the cloneId in the calls below.

    Request

    Header parameters

    • X-API-KEYstringRequired

      Your API key. Authorization: Bearer <key> is accepted as well.

    Responses

    200

    An array of clones. A workspace with no clones returns [].

    • iduuid

      The clone.

    • namestring

      The clone's name.

    401

    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"
    Response 200
    [
      {
        "id": "c1e92bf4-6b1b-4a1a-8ca5-762c28f90c97",
        "name": "Nutrition Coach"
      }
    ]

    Subscribe to an event

    POSThttps://api.personify.fyi/functions/v1/zapier/subscribe

    Starts sending one event from one clone to a URL. Zapier calls this when a Zap is switched on.

    Request

    Header parameters

    • X-API-KEYstringRequired

      Your API key. Authorization: Bearer <key> is accepted as well.

    • Content-Typestring

      application/json

    Body parameters

    • targetUrlstringRequired

      The HTTPS URL that should receive the event. hookUrl is accepted as another name for it.

    • eventstringRequired

      lead.created or conversation.ended.

    • cloneIduuidRequired

      A clone id from 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

    201

    The subscription exists.

    • iduuid

      The subscription. Keep it: it is what POST /unsubscribe needs.

    400

    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"
    401

    The key is missing, wrong or revoked.

    • "error": "An API key is required"
    • "error": "That API key is not valid"
    409

    The workspace already has 25 subscriptions. Remove a subscription you no longer use, then try again.

    • errorstring

      You have reached 25 live subscriptions

    • detailstring

      Turn 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"
      }'
    Response 201
    {
      "id": "7b3e6f10-2c4d-4e8a-b1f2-9d0c3a5e7f61"
    }

    Unsubscribe

    POSThttps://api.personify.fyi/functions/v1/zapier/unsubscribe

    Stops a subscription. Zapier calls this when a Zap is switched off or deleted. DELETE /unsubscribe does the same thing.

    Request

    Header parameters

    • X-API-KEYstringRequired

      Your API key. Authorization: Bearer <key> is accepted as well.

    • Content-Typestring

      application/json

    Body parameters

    • iduuidRequired

      The id from POST /subscribe. It can be sent as a query parameter instead, /unsubscribe?id=....

    Responses

    200

    Also 200 when no subscription in your workspace has that id, so calling it twice is harmless.

    • okboolean

      Always true.

    400

    No id was sent.

    • "error": "id is required"
    401

    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"
      }'
    Response 200
    {
      "ok": true
    }

    Sample leads

    GEThttps://api.personify.fyi/functions/v1/zapier/samples/leads

    Returns 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-KEYstringRequired

      Your API key. Authorization: Bearer <key> is accepted as well.

    Query parameters

    • cloneIduuidRequired

      A clone id from GET /clones.

    Responses

    200

    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
    • iduuid

      The same value as lead_id.

    • lead_iduuid

      The lead.

    • clone_iduuid

      The clone that captured it.

    • clone_namestring

      That clone's name.

    • conversation_iduuid or null

      The conversation the lead came from.

    • emailstring or null

      The email address the visitor gave.

    • namestring or null

      Only filled in when the clone's form asks for a name.

    • outcomestring

      One of pending, qualified, nurture, disqualified or booked. The default is pending.

    • source_urlstring or null

      The page the lead came from.

    • utm_sourcestring or null

      UTM source recorded with the lead.

    • utm_mediumstring or null

      UTM medium recorded with the lead.

    • utm_campaignstring or null

      UTM campaign recorded with the lead.

    • created_attimestamp

      When the lead was saved.

    400

    The cloneId is missing or not a clone in your workspace.

    • "error": "That clone is not in your workspace"
    401

    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"
    Response 200
    [
      {
        "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

    GEThttps://api.personify.fyi/functions/v1/zapier/samples/conversations

    Returns 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-KEYstringRequired

      Your API key. Authorization: Bearer <key> is accepted as well.

    Query parameters

    • cloneIduuidRequired

      A clone id from GET /clones.

    Responses

    200

    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
    • iduuid

      The same value as conversation_id.

    • conversation_iduuid

      The conversation.

    • clone_iduuid

      The clone that held it.

    • clone_namestring

      That clone's name.

    • lead_iduuid or null

      The lead captured in this conversation, if there was one.

    • started_attimestamp

      When the conversation started.

    • ended_attimestamp

      The time of the last message, not the time this was sent.

    • message_countinteger

      How many messages the conversation has.

    • duration_secondsinteger

      Seconds from started_at to ended_at.

    • summarystring or null

      A one-line summary of the conversation. Null when none was written.

    • page_urlstring or null

      The page the conversation happened on.

    • referrer_urlstring or null

      The referring URL recorded for the conversation.

    • transcriptarray of objects

      The messages, oldest first.

      Each item

      • rolestring

        user for the visitor and assistant for the clone. system and agent are also possible values.

      • contentstring

        The message, cut to its first 4,000 characters.

      • created_attimestamp

        When the message was created.

      • iduuid

        The message.

    • transcript_truncatedboolean

      true when messages were left off the end of transcript.

    400

    The cloneId is missing or not a clone in your workspace.

    • "error": "That clone is not in your workspace"
    401

    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"
    Response 200
    [
      {
        "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

    • eventstring

      lead.created or conversation.ended.

    • created_atstring

      When the delivery was created, in UTC, to the second. For example 2026-09-16T11:08:37Z.

    • testboolean

      true when the event came from a test rather than a real visitor, such as a chat the clone's owner held while trying it out.

    • dataobject

      The event's own fields, listed under each event below.

    Headers

    • Content-Typestring

      application/json

    • User-Agentstring

      Personify-Webhooks/1

    • X-Personify-Eventstring

      The event name, the same as event in the body.

    • X-Personify-Deliveryuuid

      A UUID for this delivery. A retry keeps the same value, so use it to skip a delivery you have already handled.

    • X-Personify-Signaturestring

      t=<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 2xx response 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.
    Example delivery
    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_iduuid

      The lead.

    • clone_iduuid

      The clone that captured it.

    • clone_namestring

      That clone's name.

    • conversation_iduuid or null

      The conversation the lead came from.

    • emailstring or null

      The email address the visitor gave.

    • namestring or null

      Only filled in when the clone's form asks for a name.

    • outcomestring

      One of pending, qualified, nurture, disqualified or booked. The default is pending.

    • source_urlstring or null

      The page the lead came from.

    • utm_sourcestring or null

      UTM source recorded with the lead.

    • utm_mediumstring or null

      UTM medium recorded with the lead.

    • utm_campaignstring or null

      UTM campaign recorded with the lead.

    • created_attimestamp

      When the lead was saved.

    Example payload
    {
      "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_iduuid

      The conversation.

    • clone_iduuid

      The clone that held it.

    • clone_namestring

      That clone's name.

    • lead_iduuid or null

      The lead captured in this conversation, if there was one.

    • started_attimestamp

      When the conversation started.

    • ended_attimestamp

      The time of the last message, not the time this was sent.

    • message_countinteger

      How many messages the conversation has.

    • duration_secondsinteger

      Seconds from started_at to ended_at.

    • summarystring or null

      A one-line summary of the conversation. Null when none was written.

    • page_urlstring or null

      The page the conversation happened on.

    • referrer_urlstring or null

      The referring URL recorded for the conversation.

    • transcriptarray of objects

      The messages, oldest first.

      Each item

      • rolestring

        user for the visitor and assistant for the clone. system and agent are also possible values.

      • contentstring

        The message, cut to its first 4,000 characters.

      • created_attimestamp

        When the message was created.

      • iduuid

        The message.

    • transcript_truncatedboolean

      true when messages were left off the end of transcript.

    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.

    Example payload
    {
      "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.