Webhooks
Opt-in push notifications. Submit with a webhook_url and webhook_secret, and the platform will POST the result to your endpoint when it is ready. No polling needed.
How it works
Section titled “How it works”- You submit code with
webhook_urlandwebhook_secret - Your code executes in the sandbox
- The platform POSTs the result to your URL with HMAC-SHA256 signature headers
- You verify the signature and process the result
Example
Section titled “Example”curl -X POST "https://api.rustbox.orkait.com/api/submit" \ -H "Content-Type: application/json" \ -H "X-API-Key: rb_live_your_key_here" \ -d '{ "language": "python", "code": "print(42)", "webhook_url": "https://your-app.com/hooks/result", "webhook_secret": "your-hmac-secret" }'Webhook payload
Section titled “Webhook payload”When execution completes, the platform POSTs the full result to your URL:
{ "id": "a0928c83-0a1d-4f5e-b724-7ecad619538f", "language": "python", "job_status": "completed", "schema_version": "1.0", "verdict": "AC", "exit_code": 0, "signal": null, "stdout": "42\n", "stderr": "", "output_integrity": "complete", "error_message": null, "cpu_time_secs": 0.009, "wall_time_secs": 0.025, "memory_peak_bytes": 3858432, "evidence": { "verdict_cause": "normal_exit", "verdict_actor": "runtime", "isolation_mode": "strict", "controls_applied": ["pid_namespace", "mount_namespace", "network_namespace", "memory_limit", "process_limit", "no_new_privileges"], "controls_missing": [], "cgroup": { "memory_limit_bytes": 268435456, "memory_peak_bytes": 3858432, "oom_events": 0, "oom_kill_events": 0, "cpu_usage_usec": 9000, "process_count": 0, "process_limit": 10 }, "timing": { "cpu_ms": 9, "wall_ms": 25, "cpu_wall_ratio": 0.36, "divergence": "cpu_bound" }, "process_lifecycle": { "reap_status": "clean", "descendant_containment": "ok", "zombie_count": 0 }, "judge_actions": [], "collection_errors": [] }, "created_at": "2026-04-02T12:00:00.000Z", "started_at": "2026-04-02T12:00:00.001Z", "completed_at": "2026-04-02T12:00:00.026Z"}Signature verification
Section titled “Signature verification”We follow the Standard Webhooks specification - the same pattern used by OpenAI, Stripe, and Svix.
Headers
Section titled “Headers”| Header | Value |
|---|---|
webhook-id | Unique message ID (the submission UUID) |
webhook-timestamp | Unix timestamp (seconds) |
webhook-signature | v1,<base64-encoded-hmac> |
Signed content
Section titled “Signed content”The HMAC-SHA256 is computed over:
{webhook-id}.{webhook-timestamp}.{body}Verification example (Python)
Section titled “Verification example (Python)”import hmac, hashlib, base64
def verify_webhook(body: bytes, headers: dict, secret: str) -> bool: msg_id = headers["webhook-id"] timestamp = headers["webhook-timestamp"] signature = headers["webhook-signature"]
signed_content = f"{msg_id}.{timestamp}.".encode() + body expected = hmac.new( secret.encode(), signed_content, hashlib.sha256 ).digest() expected_b64 = "v1," + base64.b64encode(expected).decode()
return hmac.compare_digest(signature, expected_b64)Delivery behaviour
Section titled “Delivery behaviour”- Webhooks are opt-in. No URL, no webhook.
- Secret is mandatory when URL is provided. The platform does not deliver unsigned webhooks.
- HTTPS required. Webhook URLs must use HTTPS.
- Non-blocking delivery. Webhook failures do not affect the submission result.
- 3 attempts. Immediate, then 1s delay, then 5s delay. Server errors (5xx) trigger retry; client errors (4xx) do not. Poll
/api/result/{id}as fallback if all attempts fail.
SSRF protection
Section titled “SSRF protection”Webhook URLs are validated:
- Must use HTTPS
- Private IPs blocked:
10.x,172.16-31.x,192.168.x,169.254.x - Loopback blocked:
127.0.0.1,::1,localhost