Correlation IDs & Tracing (Connect Your App Logs to Delivery Runs)

Correlation IDs & Tracing (Connect Your App Logs to Delivery Runs)

Correlation IDs turn an ocean of logs into a single trace. This guide shows how to add, propagate, and log correlation IDs so you can trace delivery attempts and retries back to the exact business event.

You’ll implement correlation_id at the producer and consumer, and learn the queries to match SendPromptly runs with your application logs.

What you’re trying to solve

You want to answer: “Which attempt caused the side effect?” and “Did a retry re-run business logic?”

“Which attempt caused the side effect?”

Correlation IDs let you map a delivery run and attempt to the exact request in your app logs.

“Did the retry re-run business logic?”

Use idempotency + correlation markers to detect dedupe vs actual re-processing.

Common gotcha: Generating a new correlation ID at each hop — that breaks trace continuity.

The 3 IDs you should standardize

Standardize these three IDs in your events and logs.

Correlation ID (business trace)

Human-readable trace for searching and dashboards.

Idempotency key (request dedupe)

Prevents duplicate ingestion and is used to dedupe replayed deliveries.

Provider message id (email/webhook external reference)

Useful for diagnosing third-party provider issues.

Micro checklist:

  • Emit correlation_id in the event payload
  • Use idempotency_key for dedupe
  • Store provider IDs where available

Where to generate correlation IDs

Prefer the producer; fallback to ingestion edge; consumer-only is last resort.

At the event producer (best)

Generate once and reuse across downstream calls.

At ingestion edge (acceptable)

If the caller didn’t set one, assign one at the API boundary.

At consumer (fallback)

If missing upstream, the consumer should create and log one for traceability.

Mini incident: A missing correlation_id at ingestion required merging logs by timestamps — adding the header at the producer fixed future incidents.

Laravel producer implementation

Add correlation_id into payload

1
2
3
4
5
6
7
8
9
$correlationId = (string) \Illuminate\Support\Str::uuid();
$idempotencyKey = "order.created:{$order->id}";

$payload = [
  'order_id' => $order->id,
  'correlation_id' => $correlationId,
];

\Log::info('event.sent', ['correlation_id' => $correlationId, 'idempotency_key' => $idempotencyKey]);

Send one Sample Project event with a correlation_id and verify you can trace it in Message Log.

Laravel consumer implementation

Log correlation ID + run/attempt identifiers

1
2
3
4
5
6
if ($inserted === 0) {
  \Log::info('webhook.deduped', ['correlation_id' => $cid, 'event_id' => $eventId]);
  return response()->json(['ok' => true], 200);
}

\Log::info('webhook.queued', ['correlation_id' => $cid, 'event_id' => $eventId]);

Store correlation ID in inbox table

Persist the correlation ID with the inbound event so you can join it to processing logs.

Common gotcha: Not logging correlation IDs — they exist but aren’t searchable.

Retry-aware tracing

Success is 2xx; retries happen on failure

Treat retries as expected behavior; only investigate sustained failure patterns.

“Deduped” vs “Processed” log markers

Use clear log markers (webhook.deduped vs webhook.processed) so dashboards and alerts can distinguish retries from duplicate processing.

Mini checklist:

  • Tag logs with correlation_id and event_id
  • Emit webhook.deduped on dedupe
  • Emit webhook.processed when side effect runs

Test steps

1
2
3
4
5
6
7
8
9
curl -i -X POST https://app.sendpromptly.com/api/v1/events \
  -H "Authorization: Bearer sp_dev_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: trace-001" \
  -H "Content-Type: application/json" \
  -d '{
    "event_key":"order.created",
    "recipient":{"email":"[email protected]"},
    "payload":{"order_id":"O-2001","correlation_id":"cid-2001"}
  }'

Expected:

  • You can search cid-2001 in Message Log
  • You can grep cid-2001 in your app logs and see event.sent, webhook.queued, webhook.deduped (if retried)

Correlation tracing failure modes

  • Generating a new correlation ID at each hop (breaks continuity).
  • Not logging correlation IDs (they exist but aren’t searchable).
  • Confusing correlation ID with idempotency key (they solve different problems).
  • Replaying deliveries without dedupe → tracing shows duplicates.
  • Not accounting for retries (multiple deliveries may be normal).

Retries and success rules matter for tracing: webhook delivery succeeds only on HTTP 2xx; failures retry with exponential backoff.

Key takeaways

  • Emit a stable correlation_id at the producer and reuse it everywhere.
  • Use idempotency_key for dedupe; correlation_id for tracing.
  • Log explicit deduped and processed markers so dashboards are accurate.
  • Use Message Log + your app logs to find the exact attempt.

Correlation IDs turn debugging into search—use Message Log + your logs to pinpoint the failing attempt.