Skip to main content

Verify a callback came from Travila

Section ID: Guide/jc8v65tbpk#verify-a-callback-came-from-travila

Build the receiver behind a daily briefing or one-time reminder. Its job is to turn an authenticated scheduler firing into one durable work item, acknowledge that acceptance, and let a worker complete the business action.

Have the schedule’s expected owner, target and payload available in your application, plus a maintained JWT verifier and a durable queue or transactional outbox. Use the receiver sequence below with the deployment’s trusted signing keys and expected claim values.

Your job target is a URL on the public internet, so anything can POST to it. Every dispatch the platform makes carries a signed token proving it came from us — verify it before acting on the request.

What arrives​

Section ID: Guide/jc8v65tbpk#what-arrives

Callback example: HTTP envelope · Signed claims.

A scheduled callback is an HTTP POST to your application’s configured endpoint. It includes Authorization with a Bearer signed JWT, Content-Type application/json, and an Idempotency-Key shaped as sched:<schedule_id>:<scheduled_time_unix_ms>. Its body is the schedule’s configured payload verbatim. The endpoint path and token are placeholders. The callback body is your configured payload; it is not converted to an API request format.

Check the callback envelope and body-binding rules before accepting the work.

Recipe: accept one firing and hand work to a worker​

Section ID: Guide/jc8v65tbpk#receiver-algorithm

The following is a required sequence, not drop-in server code. Use a maintained JWT library and a durable queue or transactional outbox.

  1. Bound the HTTP body and token sizes. Require a bearer token and the expected content type.

  2. Select a trusted key by kid from the configured issuer's JWKS. Allow only the configured algorithm (RS256 in the current scheduler contract); never follow a token-supplied key URL. Bound unknown-key refresh and cache behavior.

  3. Verify the signature, exact issuer, expected audience and required time and identity claims. Require the schedule trigger, owner, tenant, project, schedule and firing identities. Return a generic authentication error without disclosing token contents if validation fails.

  4. Match the verified identities, target and payload to an allowed schedule in your system. Check that the business action is still permitted. A valid old firing is not permission to access a different recipient or revoked resource.

  5. Apply the verified firing and payload-binding rules. In one database transaction or equivalent durable queue operation, save the verified firing identity, its scope and a hash of the expected payload together with the pending work item. If an identical record already exists, return its accepted acknowledgement. If the same identity carries a different payload, reject it. If persistence fails, do not return success.

  6. Return 2xx only after durable acceptance. A worker then performs the action, using a stable operation ID and checking uncertain outcomes before retrying. Mark the action completed only after its outcome is known.

A separate already_processed() read followed by mark_processed() and a direct action is unsafe: concurrent requests race, and a crash after marking can lose the work. Likewise, acknowledging before enqueueing can lose an entire firing.

Getting the public key​

Section ID: Guide/jc8v65tbpk#getting-the-public-key

The platform publishes its signing keys as a standard JWKS, unauthenticated:

JWKS verification navigation.

with the usual discovery document alongside it:

OpenID discovery navigation.

Configure your verifier with the algorithm, key-selection and caching contract.

Claims​

Section ID: Guide/jc8v65tbpk#claims

Keep the expected owner, tenant, project and schedule identity with the registered job. Compare the callback’s signed claim set to that record before enqueueing its work; a valid signature alone does not select an authorized business action.

Audience​

Section ID: Guide/jc8v65tbpk#audience

Configure the receiver’s expected audience from its own registered URL, using the audience normalization rules. Reject a signed callback intended for another receiver instead of accepting any audience the token supplies.

Replay protection and durable acceptance​

Section ID: Guide/jc8v65tbpk#replay-protection-and-durable-acceptance

Test two concurrent deliveries of the same verified firing and confirm that they produce one durable work item. Repeat the test with a changed payload and require rejection. The firing identity and payload-binding rules define what the receiver must compare; an initial duplicate lookup without an atomic write is insufficient.

Confirm acceptance and recover delivery failures​

Section ID: Guide/jc8v65tbpk#responding

Return success only after your receiver has durably accepted the work. Follow the acknowledgement and retry contract when persistence or delivery fails. Track the worker’s business result separately.

The completed receiver rejects a callback for another schedule or user, returns the same acceptance for an identical retry, and preserves accepted work across a restart. Test those cases with controlled data before enabling the recurring job. Track business completion separately so a worker failure can be recovered without asking the scheduler to create a new firing.

Document ID: Guide/jc8v65tbpk