API Reference
Webhooks

Webhook Deliveries

When you supply a callbackUrl on an assignment, the Pharlo sends a POST request to that URL on every status change. This page documents how to inspect the delivery log and how to verify the signature of incoming payloads.

To set up a callbackUrl, see Assignments — POST /api/v1/assignments. For an end-to-end guide on receiving and handling webhooks, see the Webhooks guide.

Base URL: https://pharlo.io
Auth: Authorization: Bearer ds_live_...


Endpoints

MethodPathDescriptionStatus
GET/api/v1/webhooks/deliveriesPaginated delivery log200

GET /api/v1/webhooks/deliveries

Returns a paginated log of outbound webhook calls. Each entry represents one delivery attempt (or the latest retry) for a single assignment status change. Use this to diagnose failed deliveries and identify which callback URLs are unreachable.

The delivery log shows the latest attempt per delivery record. If the API retried after a failure, attempts reflects the total retry count and lastAttemptAt shows when the final attempt was made.

Query parameters

ParameterTypeDefaultDescription
statusstringFilter by delivery status: pending, delivered, failed
pageint1Page number
limitint20Items per page (max: 50)

Example request

curl "https://pharlo.io/api/v1/webhooks/deliveries?status=failed&limit=20" \
  -H "Authorization: Bearer $DELIVERY_API_KEY"

Response 200 OK

{
  "items": [
    {
      "id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890",
      "assignmentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "url": "https://your-server.com/webhook",
      "status": "delivered",
      "httpStatusCode": 200,
      "attempts": 1,
      "lastAttemptAt": "2026-04-23T10:02:40Z"
    },
    {
      "id": "e2f3a4b5-c6d7-8901-bcde-f12345678901",
      "assignmentId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "url": "https://your-server.com/webhook",
      "status": "failed",
      "httpStatusCode": 500,
      "attempts": 3,
      "lastAttemptAt": "2026-04-23T10:15:00Z",
      "lastError": "Connection timed out"
    }
  ],
  "total": 2,
  "page": 1,
  "limit": 20
}

Response fields

FieldTypeDescription
idUUIDDelivery record ID
assignmentIdUUIDID of the related assignment. Use with GET /api/v1/assignments/{id} to fetch full details.
urlstringThe callbackUrl that was called
statusstringpending — not yet attempted; delivered — your server responded 2xx; failed — all retries exhausted
httpStatusCodeint | nullHTTP status code returned by your server on the last attempt. null if the request never reached the server (e.g. DNS failure).
attemptsintTotal number of delivery attempts made
lastAttemptAtISO 8601Timestamp of the most recent attempt
lastErrorstring | nullHuman-readable error from the last failed attempt (e.g. "Connection timed out"). Present only when status is failed.

Delivery statuses

StatusMeaning
pendingQueued for delivery, no attempt made yet
deliveredYour endpoint responded with a 2xx status code
failedAll retry attempts exhausted — your endpoint did not return 2xx
⚠️

The API retries failed deliveries with exponential back-off. If your endpoint is temporarily unavailable, the delivery may recover on its own. Check attempts and lastAttemptAt to determine whether retries are still in progress or have stopped.


Webhook payload format

On each assignment status change, the API sends a POST request to your callbackUrl with the following JSON body:

{
  "assignmentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "published",
  "platform": "youtube",
  "platformUrl": "https://youtu.be/dQw4w9WgXcQ",
  "errors": [],
  "timestamp": "2026-04-23T10:02:35Z"
}

Payload fields

FieldTypeDescription
assignmentIdUUIDID of the assignment that changed status
statusstringNew assignment status. See Assignment lifecycle for the full list.
platformstringTarget platform (e.g. youtube, facebook)
platformUrlstring | nullLive URL of the published content. Populated only when status is published.
errorsarrayList of error objects when status is failed. Each item has code and message. Empty array otherwise.
timestampISO 8601When the status change occurred

Your endpoint must respond with a 2xx status code within 10 seconds. Responses outside this window are treated as failures and trigger a retry. Return 200 OK with an empty body — the response body is ignored.

Example — failed assignment payload

When status is failed, the errors array contains one or more entries explaining why the platform rejected the content:

{
  "assignmentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "failed",
  "platform": "youtube",
  "platformUrl": null,
  "errors": [
    {
      "code": "QUOTA_EXCEEDED",
      "message": "YouTube daily upload quota exceeded for this channel."
    }
  ],
  "timestamp": "2026-04-23T10:15:00Z"
}

Use the errors array to decide whether to fix the assignment payload via PATCH and retry, or to alert your team.


Signature verification

Every delivery includes an X-Webhook-Signature header — an HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret.

Always verify this signature before processing the payload. This prevents a third party from sending forged requests to your endpoint.

⚠️

Use the raw request body for verification — before any JSON parsing. Many frameworks offer a way to access raw bytes (e.g. request.get_data() in Flask, req.rawBody in Express). Parsing and re-serializing the JSON will produce a different byte sequence and cause verification to fail.

# Generate a test signature to compare against your implementation
SECRET="your_webhook_secret"
BODY='{"assignmentId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","status":"published"}'
echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET"

Error responses

StatusCodeReason
400VALIDATION_ERRORInvalid query parameter value
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey does not have access to this resource
{
  "error": "Validation failed",
  "details": {
    "status": ["The value 'unknown' is not a valid delivery status."]
  }
}

For a full guide on handling errors and retries, see Error Handling.


See also

  • Webhooks guide — end-to-end guide: receiving payloads, verifying signatures, and best practices
  • Assignments — where to set callbackUrl when creating a publishing job
  • Assignment lifecycle — full list of statuses that trigger webhook deliveries
  • Error Handling — how to respond to errors and implement retries