Skip to main content

Webhooks

Phonefarm sends public process callbacks using one event type:
process.updated

What Gets Sent

Example payload:
{
  "event_type": "process.updated",
  "milestone": "accepted",
  "emitted_at": "2026-03-22T08:30:00.000Z",
  "process": {
    "process_id": "3e6f2e7b-4e3c-4c7c-b8d7-0c53d83f9870",
    "process_type": "keyword_warmup",
    "request_id": "warmup-2026-03-22-001",
    "status": "queued"
  }
}

Headers

Always sent:
  • Content-Type: application/json
  • User-Agent: phonefarm-callback/1.0
Sent when PHONE_FARM_WEBHOOK_SECRET is configured:
  • X-PhoneFarm-Timestamp
  • X-PhoneFarm-Signature

Signature Verification

Signature input:
${timestamp}.${rawJsonBody}
Algorithm:
HMAC-SHA256
Example verifier:
import crypto from "node:crypto";

export function verifyPhonefarmCallback({ secret, timestamp, rawBody, signature }) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected, "utf8"),
    Buffer.from(signature, "utf8"),
  );
}

Delivery Semantics

Callbacks are best-effort:
  • Phonefarm logs callback failure
  • callback failure does not fail the originating API request
  • current edge handlers do not retry callback delivery
For correctness, your integration should:
  1. verify the signature when configured
  2. accept duplicate callback deliveries safely
  3. poll GET /processes/{process_id} for source-of-truth state

Milestones

Current public callbacks include milestones such as:
  • accepted
  • cancel_requested
  • cancelled
  • failed_terminal
Milestones are descriptive markers, not a versioned workflow engine contract. Phonefarm also emits operational telemetry toward UGC Tracker on the runtime side. Those phone-state and session events are not the same contract as public process.updated callbacks.