Connect a repository's webhooks to DataMind Oda so that an event can start work by itself: a verified delivery reaches your rules, and a rule can open a chat in a workspace, with the delivery recorded on the chat's first message. This page covers the endpoint you connect, the contract a GitHub delivery must satisfy, the answers GitHub sees, and what Oda does and does not guarantee. The step-by-step setup is on GitHub webhooks, and what a delivery creates is on Webhook sessions.
webhook-, and its first message is the rule's prompt, recorded as a webhook notice that names the delivery's provider, source, delivery and rule rather than a person.Set up the endpoint with these fields. Every one of them is required.
| Field | What it is |
|---|---|
source | A name for this endpoint, for example primary-github. Each verified delivery carries it to the rules. |
path | The endpoint's exact path, for example /. It starts with /, is not / alone, and carries no trailing slash, query or fragment. |
secretEnv | The reference to the stored secret that Oda verifies deliveries with. The secret itself is not written here. |
maxBodyBytes | The largest raw request body Oda accepts, as a positive whole number. |
A value that fails its check refuses the endpoint before it registers, with messages such as webhook-github source must be a non-empty trimmed string or webhook-github path must be an absolute non-root pathname without a trailing slash, query, or fragment.
By default the endpoint shares the address that serves the Oda interface. A deployment can give it a listener of its own on a local address instead, behind a TLS reverse proxy or tunnel. The endpoint adds no encryption of its own, so keep it local and give GitHub the public address the proxy serves.
POST requests with application/ json. The media type may carry at most one parameter, and only charset=utf-8 is accepted. Form-encoded deliveries are refused.X-Hub-Signature-256, X-GitHub-Delivery and X-GitHub-Event are required, each with exactly one non-empty value. A missing or repeated header is refused with missing <name> header (400).Content-Length must be a plain whole number, digits only. A body larger than maxBodyBytes is refused, whether its size is declared or seen while the body is read.pull_request, are checked by the rules, not the endpoint.invalid webhook signature (401), and its payload is never read. Because the secret resolves for every request, rotating it takes effect on the next delivery, with nothing reloaded.The 202 answer means only that the verified delivery was taken and dispatched in memory. It comes before the rules run, and it does not say that a rule matched or that a chat was created. The other answers:
| Status | What GitHub receives | When Oda returns it |
|---|---|---|
202 | An empty body | The delivery was verified and dispatched in memory. |
400 | missing <name> header, invalid Content-Length, request body was aborted, request body is not valid UTF-8, request body is not valid JSON, GitHub webhook payload must be a JSON object, GitHub webhook payload is not lossless JSON | A required header, the declared length, the body or the payload failed its checks. |
401 | invalid webhook signature | The signature does not match the secret. |
405 | method not allowed, with the header allow: POST | The request is not a POST. |
413 | request body is too large | The declared or streamed body exceeds maxBodyBytes. |
415 | content type must be application/ | The media type is not application/. |
503 | GitHub webhook secret is unavailable, webhook runtime is unavailable, webhook ingress is unavailable | The secret does not resolve, the part of Oda that takes deliveries is unavailable, or the request could not be handled. |
Before a rule runs, the delivery itself is checked. A delivery must carry a non-empty kind, source and id (webhook delivery kind must be a non-empty string, webhook delivery source must be a non-empty string, webhook delivery id must be a non-empty string), a non-negative whole-number receipt time (webhook delivery receivedAt must be a non-negative safe integer), and a lossless JSON payload (webhook delivery must be lossless JSON). Nothing runs when a check fails.
A rule must return nothing or a single request: webhook rule result must be null or a Session request object.
A request for a chat has its own checks. Every text value must be non-empty (webhook Session request <field> must be a non-empty string), the folder must be an absolute path (webhook Session request workspacePath must be absolute, got "<path>"), and a model, when named, must be an object whose token cap, when set, is a positive whole number (webhook Session request model must be an object, webhook Session request model.). A request that fails is refused before anything is created.
When a webhook message enters a chat, Oda checks that the chat's recorded folder matches exactly one workspace. The check fails with webhook Session "<id>" has no cwd, webhook Session "<id>" belongs to <n> Workspaces at prompt admission, or webhook Session "<id>" cwd <cwd> differs from its Workspace path.
Each rule run records one line naming its provider, source, delivery and rule, in the shape webhook: provider=.. A rule that fails adds the reason after failed:; a run stopped because the handling shut down is noted after stopped after disposal:. A failed rollback adds a warning of its own. The log never contains the secret, the signature or the payload.
Start from the delivery id. The log line for each rule run carries it, so you can see which rules saw the delivery. If a rule opened a chat, the chat's identifier starts with webhook-, and its first message is a webhook notice recording the same provider, source, delivery and rule.
Method, media type, body, headers, secret, signature, then JSON: the endpoint works through a request in that order, and the signature is verified over the raw body before anything is parsed. A delivery that fails the signature check never has its payload read.
No. Nothing queues, replays or retries a delivery: one that was refused or lost is not picked up again later, and a rule call that had not yet reached a prompt when Oda stopped is lost. Check that the work happened instead of counting on a retry.
The delivery itself is checked first: its kind, source and id must be non-empty, its receipt time must be a non-negative whole number, and its payload must be lossless JSON. A delivery that fails one of these checks is refused with the message that names the check, and nothing runs.
Every text value it carries must be non-empty, the folder must be an absolute path, and a model, when named, must be an object whose token cap, when set, is a positive whole number. Oda validates all of this before anything is changed; a request that fails is refused and leaves nothing behind.
The endpoint's secretEnv setting holds a reference, not the secret. Oda resolves that reference against its stored credentials for every request, so rotating the stored secret takes effect on the next delivery, and the raw secret never sits in the endpoint's settings. Stored credentials covers where credentials live.
No. The log records the provider, source, delivery and rule for each run, and a failure adds the reason, but the secret, the signature and the payload are never logged.
Yes. It can run as a listener of its own on a local address while the Oda interface keeps its usual address; put a TLS reverse proxy or tunnel in front, and the endpoint itself stays without encryption.
Whatever events your repository sends. The endpoint hands every verified delivery to the rules with the event's name, such as pull_request, and its payload; which events a rule acts on, and what the fields of an event must contain, is the rule's choice. The endpoint guarantees only that the delivery is authenticated and that its payload is a lossless JSON object.
Before the prompt is accepted, nothing of the chat remains: a chat that was already attached is detached and disposed, and a folder registered during the attempt can stay registered, because another caller may already use it. The rollbacks are best effort, and a failed rollback is logged.