Deploy and triggers
Deploy a workflow with one call and start runs from a trigger URL.
Deploy and trigger
The deploy body is the workflow's source, up to 512 KiB. Send TypeScript as is, or Python with content-type: text/x-python. Workflow names are lowercase letters, digits and hyphens, up to 63 characters. Each deploy is scanned. Blocked code is refused with the reason, and warnings are returned in warnings. Python is loaded once at deploy time. A syntax error or an import that is not installed fails the deploy with 400 and the traceback. A Python deploy with new packages returns 202 with a deployId while they build.
# Deploy (developer or admin key). Answers {"versionHash", "sizeBytes", "warnings"}.
curl -X PUT https://api.shiftlabs.org/v1/workflows/refund-check \
-H "authorization: Bearer $SHIFTLABS_API_KEY" \
--data-binary @refund-check.ts
# The same for Python.
curl -X PUT https://api.shiftlabs.org/v1/workflows/refund-check \
-H "authorization: Bearer $SHIFTLABS_API_KEY" -H "content-type: text/x-python" \
--data-binary @refund_check.py
# Create a trigger. Answers 201 {"triggerId", "url", "signingScheme"}.
curl -X POST https://api.shiftlabs.org/v1/triggers \
-H "authorization: Bearer $SHIFTLABS_API_KEY" -H "content-type: application/json" \
-d '{"workflow": "refund-check"}'
# Start a run from any webhook sender. Answers 202 {"runId"}; sent again with the same
# Idempotency-Key within 7 days, it answers 200 {"duplicate": true} and starts nothing.
curl -X POST https://hooks.shiftlabs.org/t/<triggerId> \
-H "content-type: application/json" -H "idempotency-key: ch_123" -d '{"id": "ch_123"}'
# Follow it.
curl https://api.shiftlabs.org/v1/runs/<runId>/timeline \
-H "authorization: Bearer $SHIFTLABS_API_KEY"
A trigger id is 128 random bits. Without signing, anyone with the URL can start a run. Rotate it with POST /v1/triggers/:id/rotate or revoke it with DELETE. To require signed deliveries, create the trigger with "signing": {"scheme": …}. Timestamped signatures must be within 5 minutes. A webhook body is capped at 256 KiB.
A delivery the trigger has seen in the last 7 days returns 200 {"duplicate": true} and starts no run. Seen means the same shiftlabs signature, the same Stripe event id (Stripe signs each retry of an event again), the same X-GitHub-Delivery, or, on any trigger, the same Idempotency-Key header, which any sender can set (1 to 255 visible ASCII characters).
Revisions · refund-check
2| Rev | Hash | Deployed |
|---|---|---|
| B current | f7e85236 | 2026-10-20 |
| A | 1d9a281e | 2026-10-20 |
Runs keep the hash they started on. Redeploying a known hash keeps its letter.
| Scheme | Secret | Header checked |
|---|---|---|
shiftlabs | Generated and returned once, as signingSecret. | Shiftlabs-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of <t>.<body>. |
stripe | Your Stripe endpoint secret, 8–256 characters. | Stripe-Signature |
github | Your GitHub webhook secret, 8–256 characters. | X-Hub-Signature-256 and X-GitHub-Delivery |