Petnow LogoPetnow

Events & Payloads

The webhook event types, the event envelope, and an example body for each job kind.

Event types

Event typeSent when
fingerprint_addition_job.succeededA registration (addFingerprints) job succeeded.
fingerprint_addition_job.failedA registration job failed.
verification_job.succeededA verification job succeeded.
verification_job.failedA verification job failed.
identification_job.succeededAn identification job succeeded.
identification_job.failedAn identification job failed.
webhook.testYou click Send test event in the Console. You cannot subscribe to it.

You choose which of the six job event types an endpoint receives, or select All events (including ones added later).

Jobs created by a hosted session send no events. Jobs that Web Integration sessions create send no webhook events today. Webhooks cover jobs started through the Server API.

Ignore event types you do not know. Petify can add event types later, and an endpoint that subscribes to All events receives them. Acknowledge an unknown type with a 2xx status and do nothing else. Do not answer with an error.

Event envelope

Every event is a JSON object with four fields.

FieldTypeDescription
idstringUnique event ID, such as evt_3f2a9c1d0b8e4f6a9d7c5b3a1e0f2d4c. It equals the webhook-id header and stays the same across retries and resends.
typestringThe event type.
createdAtstringWhen Petify emitted the event (ISO 8601). This is not the time of a delivery attempt.
dataobjectThe job result. See below.

data always carries jobId. It mirrors what the job's poll endpoint returns in its data at the time of the event, with these additions:

  • Registration and verification events also carry petId.
  • On a failure, result fields that a poll would not fill are null.
  • Field names are camelCase, as in the v2 API responses.

The event carries the job's status and result at the moment it was emitted. In a rare re-run, a job can send *.failed first and *.succeeded later. When in doubt, trust the latest answer from the job's poll endpoint.

Example bodies

Registration

fingerprint_addition_job.succeeded and fingerprint_addition_job.failed share one shape. Only type and status differ.

{
  "id": "evt_3f2a9c1d0b8e4f6a9d7c5b3a1e0f2d4c",
  "type": "fingerprint_addition_job.succeeded",
  "createdAt": "2026-10-20T09:14:52.318Z",
  "data": {
    "jobId": "6b1f0c1e-7d3a-4c58-9b0e-2f4a8d6c1e35",
    "petId": "0d4e7a92-5c31-4f8b-a6d7-91b3c2e8f470",
    "status": "SUCCESS"
  }
}

Verification

{
  "id": "evt_9a7c5e3b1d2f4a6c8e0b1d3f5a7c9e2b",
  "type": "verification_job.succeeded",
  "createdAt": "2026-10-20T09:21:07.642Z",
  "data": {
    "jobId": "c2d84f1a-93b6-4e0d-8a57-6f1b3c9d2e48",
    "petId": "0d4e7a92-5c31-4f8b-a6d7-91b3c2e8f470",
    "status": "SUCCESS",
    "isVerified": true,
    "score": 95
  }
}

When a verification job fails, isVerified and score are null.

{
  "id": "evt_1b3d5f7a9c2e4b6d8f0a2c4e6b8d0f1a",
  "type": "verification_job.failed",
  "createdAt": "2026-10-20T09:22:31.105Z",
  "data": {
    "jobId": "e7a19b3c-4d52-4f86-b0c1-8a3d5e7f9b21",
    "petId": "0d4e7a92-5c31-4f8b-a6d7-91b3c2e8f470",
    "status": "FAILED",
    "isVerified": null,
    "score": null
  }
}

Identification

pets lists every result row of the job, highest score first, as the poll endpoint returns it. metadata is a JSON string.

{
  "id": "evt_5c7e9a1b3d4f4c6e8a0b2d4f6c8e0a1b",
  "type": "identification_job.succeeded",
  "createdAt": "2026-10-20T09:30:44.870Z",
  "data": {
    "jobId": "4f8a2c6e-1b9d-4e73-a5c0-3d7f9b1e2a64",
    "status": "SUCCESS",
    "pets": [
      {
        "id": "0d4e7a92-5c31-4f8b-a6d7-91b3c2e8f470",
        "score": 95,
        "metadata": "{\"name\": \"Buddy\", \"owner\": \"John Doe\"}"
      },
      {
        "id": "a93c5e71-2b4d-4f60-8e1a-7c9d3b5f0e82",
        "score": 78,
        "metadata": "{\"name\": \"Max\", \"owner\": \"Jane Smith\"}"
      }
    ]
  }
}

When an identification job fails, pets is null.

{
  "id": "evt_7e9a1c3b5d6f4e8a0c2b4d6f8a0c2e3b",
  "type": "identification_job.failed",
  "createdAt": "2026-10-20T09:31:12.236Z",
  "data": {
    "jobId": "8d2f6a0c-5e3b-4a91-b7d4-1c9e3f5a7b20",
    "status": "FAILED",
    "pets": null
  }
}

Test event

webhook.test has a data.message field and no job fields.

{
  "id": "evt_2d4f6a8c0e1b4d3f5a7c9e1b3d5f7a9c",
  "type": "webhook.test",
  "createdAt": "2026-10-20T09:05:00.000Z",
  "data": {
    "message": "This is a test event from Petify."
  }
}

Handling an event

  1. Verify the signature against the raw request body.
  2. Check type. Ignore types you do not handle, and answer 2xx.
  3. Deduplicate on webhook-id (the id field), because delivery is at least once.
  4. Store or queue the work, then answer 2xx within 10 seconds.

On this page