Talk to your AI agent to run Simetrik. →
SimetrikDocs

Status notifications

Register an endpoint and Simetrik calls you as each file moves — signed, authenticated, retried, and correlated with your own track id.

JaaS has no status endpoint to poll. Instead, you register a notification_config on the source and Simetrik posts to your endpoint every time a file changes state. It works identically for file ingestion and the webhook flow.

You register it in the same call that creates the source — either POST /api/v2/sources or POST /api/v2/webhooks.

Configuration

FieldTypeRequiredWhat it does
target_urlstringYesWhere to post.
eventsarrayYesWhich events you want. Anything not listed is never sent.
auth_configobjectYesHow Simetrik authenticates against your endpoint.
signing_secretstringNoSecret used to sign the payload.
inbound_track_id_header_namestringNoHeader to read your track id from. Default X-Correlation-Id.
outbound_track_id_field_keystringNoPayload key the track id comes back under. Default correlation_id.
{
  "notification_config": {
    "target_url": "https://hooks.example.com/simetrik/files",
    "events": [
      "download_status.completed",
      "download_status.download_failed",
      "download_status.validation_failed",
      "download_status.processing_failed"
    ],
    "auth_config": {"type": "bearer", "token": "…"},
    "signing_secret": "…",
    "inbound_track_id_header_name": "X-Trace-Id",
    "outbound_track_id_field_key": "trace_id"
  }
}

Set your own signing_secret. If you leave it out one is generated for you, and it is never returned in the response — which leaves you with signed payloads you have no way to verify.

Events

EventFires when
download_status.processingThe file is moving through the pipeline. Fires more than once per file.
download_status.completedThe file was delivered and reconciliation was notified.
download_status.download_failedThe file couldn't be read or fetched.
download_status.validation_failedThe content couldn't be parsed. File ingestion only.
download_status.processing_failedDelivery or the downstream notification failed.

Which internal state maps to which event is listed per flow, in file ingestion and in the webhook flow.

The payload

{
  "event_type": "download_status.completed",
  "timestamp": "2026-09-14T18:04:21.512890+00:00",
  "trace_id": "settlement-2026-09-14-run-3",
  "data": {
    "webhook_file_id": "b8f1c2e4-…",
    "webhook_source_id": "61171_wh_9d200105",
    "source_id": "61171",
    "filename": "settlements-2026-09-14.csv",
    "file_url": "https://files.example.com/settlements/2026-09-14.csv",
    "status": "completed",
    "file_size_bytes": 184320,
    "downloaded_at": "2026-09-14T18:04:19.004112",
    "file_resource_path": "…/61171/8c3e….csv"
  }
}
FieldAlways presentWhat it holds
event_typeYesThe event.
timestampYesWhen the notification was built, UTC.
trace_idNoYour track id, under the key you configured — correlation_id unless you set outbound_track_id_field_key, as the source above did. Webhook flow only, and only when you sent one.
data.webhook_file_idYesThe file's id in the webhook flow; the batch file name in file ingestion.
data.webhook_source_idYesThe webhook id in the webhook flow; the source id in file ingestion.
data.source_idYesThe source, as a string.
data.filenameYesThe file name. Generated by Simetrik in file ingestion, since you never named it.
data.file_urlYesWhere the file came from. Empty string when it doesn't apply.
data.statusYesThe last segment of event_type.
data.file_size_bytesNoSize, once known.
data.downloaded_atNoWhen the download finished.
data.s3_key, data.file_resource_pathNoWhere the file was delivered inside Simetrik. Useful when raising a support ticket.
data.error.messageNoPresent on failure events only.

Headers

HeaderValue
X-Webhook-EventThe event type.
X-Webhook-Delivery-IdA UUID for this delivery. Stable across retries — use it to discard duplicates.
X-Webhook-Signaturesha256=<hex digest>.
X-Trace-IdYour track id, under the header derived from outbound_track_id_field_key — X-Correlation-Id by default, X-Trace-Id for the source above. Only when you sent one.

Verifying the signature

The signature is an HMAC-SHA256 over the payload serialized with its keys sorted, using your signing_secret. Since the body arrives in its natural order, re-serialize it before computing the digest:

import hashlib
import hmac
import json


def is_authentic(raw_body: bytes, signature_header: str, signing_secret: str) -> bool:
    payload = json.loads(raw_body)
    canonical = json.dumps(payload, sort_keys=True, ensure_ascii=True).encode("utf-8")
    expected = hmac.new(signing_secret.encode("utf-8"), canonical, hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature_header)

Outside Python, three details have to match exactly, or the digest won't:

  • Keys sorted at every level.
  • , between entries and : after each key — the separators Python writes by default.
  • Non-ASCII escaped as \uXXXX. This is the one that bites: a filename with an accent is signed in its escaped form, so a serializer that emits raw UTF-8 produces a different digest. ensure_ascii=True is Python's default and is passed explicitly above to make the requirement visible.

The track id reaches these notifications from the webhook flow only — file ingestion batches records before processing, so there's no inbound call to carry one. It sits at the top level and is only present when you sent one. That's deliberate: clients that don't use it get a signature computed over exactly the same fields as before.

Authenticating against your endpoint

auth_config.typeFieldsWhat is sent
none—Nothing.
basic_authusername, passwordAuthorization: Basic …
api_keyapi_key, api_key_headerYour key in the header you name. Defaults to X-API-Key.
bearertokenAuthorization: Bearer …
oauth2_client_credentialstoken_url, client_id, client_secret, scopeA client-credentials token, fetched and cached until shortly before it expires, then sent as a bearer token.

Delivery and retries

  • 10 seconds per attempt, 3 attempts maximum.
  • Retried on network errors and on 5xx. Backoff is exponential with jitter.
  • Not retried on 4xx — that's read as a permanent rejection.
  • X-Webhook-Delivery-Id stays the same across retries, so deduplicate on it.

Notifications are fire-and-forget: a delivery that fails every attempt is logged on the Simetrik side and dropped. It never affects the file, which is already on its way. So treat these notifications as a fast path, not as the ledger — if one never arrives, the file may well have been delivered anyway.

Answer with any 2xx as soon as you've accepted the payload, and do your own work afterwards. Anything slower than 10 seconds reads as a failed attempt and gets retried.

On this page