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
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/v1/webhooks/deliveries | Paginated delivery log | 200 |
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
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by delivery status: pending, delivered, failed |
page | int | 1 | Page number |
limit | int | 20 | Items 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
| Field | Type | Description |
|---|---|---|
id | UUID | Delivery record ID |
assignmentId | UUID | ID of the related assignment. Use with GET /api/v1/assignments/{id} to fetch full details. |
url | string | The callbackUrl that was called |
status | string | pending — not yet attempted; delivered — your server responded 2xx; failed — all retries exhausted |
httpStatusCode | int | null | HTTP status code returned by your server on the last attempt. null if the request never reached the server (e.g. DNS failure). |
attempts | int | Total number of delivery attempts made |
lastAttemptAt | ISO 8601 | Timestamp of the most recent attempt |
lastError | string | null | Human-readable error from the last failed attempt (e.g. "Connection timed out"). Present only when status is failed. |
Delivery statuses
| Status | Meaning |
|---|---|
pending | Queued for delivery, no attempt made yet |
delivered | Your endpoint responded with a 2xx status code |
failed | All 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
| Field | Type | Description |
|---|---|---|
assignmentId | UUID | ID of the assignment that changed status |
status | string | New assignment status. See Assignment lifecycle for the full list. |
platform | string | Target platform (e.g. youtube, facebook) |
platformUrl | string | null | Live URL of the published content. Populated only when status is published. |
errors | array | List of error objects when status is failed. Each item has code and message. Empty array otherwise. |
timestamp | ISO 8601 | When 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
| Status | Code | Reason |
|---|---|---|
400 | VALIDATION_ERROR | Invalid query parameter value |
401 | UNAUTHORIZED | Missing or invalid API key |
403 | FORBIDDEN | Key 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
callbackUrlwhen creating a publishing job - Assignment lifecycle — full list of statuses that trigger webhook deliveries
- Error Handling — how to respond to errors and implement retries