Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.titanx.io/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This documents the job.completed webhook callback that TitanX sends to your configured endpoint once an entire job has been fully finalized — meaning all contacts in that job have been processed and scored.
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
  • No contacts enriched — all contacts were rejected during enrichment (job finalized as “Not Enriched”)
In both cases jobStatus will be "Finalized". Timing depends on job size and current processing load.
If you want a real-time per-contact signal, use job.contact.scored. Use job.completed when you need a single notification that the entire batch is ready.

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",
    "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;

    // Fetch all contacts for this job via the Jobs API if needed
    // GET /api/public/v2/jobs/{jobId}/contacts
    console.log(`Job "${jobName}" finalized at ${modifiedAt}`);
  }
});
After receiving job.completed, use GET /api/public/v2/jobs/{jobId}/contacts to pull the full result set for the job.

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.