Skip to main content

Overview

This documents the job.completed webhook callback that TitanX sends to your configured endpoint once an entire job has reached its terminal state. It is a job-level bookkeeping signal — it confirms a batch is done but carries no contact data. Each contact’s scored result was already delivered to you individually, in real time, via job.contact.scored as it was scored. A job is simply an organizational grouping of contacts; job.completed plays no role in data delivery.
Related Documentation:

When Does This Fire?

job.completed fires once per job when the job reaches a terminal state. This happens in one of two scenarios:
  • All contacts scored — every contact in the job has been fully processed and scored (jobStatus: "Finalized")
  • No contacts enriched — all contacts were rejected during enrichment, so the job is finalized as “Not Enriched” (jobStatus: "Not Enriched")
Timing depends on job size and current processing load.
job.contact.scored and job.completed are not alternatives. job.contact.scored is how you receive results — every contact fires its own event in real time as it is scored. job.completed delivers no contact data; use it for job-level bookkeeping, such as marking a batch complete in your UI once scoring is done.

POST job.completed

TitanX sends this POST request to your configured webhook URL when a job is fully finalized.

Headers

X-TitanX-Signature
string
required
HMAC-SHA256 signature (base64) of the request body. Always verify this before processing.Example: jdoe+XYZ123abc/def456GHI789==
Content-Type
string
required
Always application/json
User-Agent
string
Example: TitanX-Webhooks/1.0

Request Body

payload
object
required
Job summary data.
eventType
string
required
Always "job.completed" for this callback.
timestamp
number
required
Unix timestamp in milliseconds when the event occurred.Example: 1705318200000
apiVersion
string
required
API version. Currently "v2".
id
string
required
Unique identifier for this webhook delivery (UUID). Use this for idempotency.Example: "7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d"

Expected Response

Your endpoint should return a 2xx status code within 10 seconds.
Status CodeBehavior
200-299Success — no retry
400-499Client error — no retry (except 408, 429)
408, 429Will retry
500-599Server error — will retry with exponential backoff

Example Payload

{
  "payload": {
    "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "jobType": "scoring",
    "jobName": "Q1 Outbound Campaign",
    "jobStatus": "Finalized",
    "modifiedAt": "2024-01-15T10:35:00Z"
  },
  "eventType": "job.completed",
  "timestamp": 1705318200000,
  "apiVersion": "v2",
  "id": "7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d"
}

Handling the Event

app.post('/webhooks/titanx', (req, res) => {
  // 1. Verify signature (always do this first)
  if (!verifySignature(req)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const { eventType, payload, id } = req.body;

  // 2. Acknowledge immediately
  res.status(200).json({ received: true });

  // 3. Process asynchronously
  if (eventType === 'job.completed') {
    const { jobId, jobName, jobStatus, modifiedAt } = payload;

    // Contacts already arrived individually via job.contact.scored.
    // Do job-level bookkeeping here (e.g. mark this batch complete in your UI).
    console.log(`Job "${jobName}" finished scoring at ${modifiedAt}`);
  }
});
job.completed contains no contact data — you already have every contact from the per-contact job.contact.scored events, and the job is just an organizational grouping. If you need to reconcile or backfill a missed delivery, you can optionally call GET /api/public/v2/jobs/{jobId}/contacts, but treat it as a fallback rather than the primary way to get results.

Security

All webhook requests include X-TitanX-Signature. See Verifying Webhook Signatures for implementation examples in Node.js and Python.

OpenAPI Definition

The complete OpenAPI 3.1 specification for this webhook callback is available in the openapi.json file under the webhooks section:
webhooks:
  job.completed:
    post:
      summary: job.completed webhook callback
      # Full specification in openapi.json
The webhook payload uses the JobCompletedWebhookEventPayload schema defined in the OpenAPI spec. This schema is generated directly from the application’s TypeScript types to ensure the documentation always matches the actual implementation.