Incoming webhooks are available only in Restate Cloud. They’re not yet available in Restate BYOC.
Incoming webhooks are in preview.
POST request invokes the handler with the request body as input.
Create a webhook
1
Register the target service
A webhook invokes one handler of a public service.
Register the deployment that contains the handler before you create the webhook.Virtual objects and workflows can’t be webhook targets. To process events per key, forward them to a virtual object.
2
Create the webhook
In the Restate Cloud UI, open your environment’s Integration & API keys page and create an incoming webhook.
Choose the service, the handler, and the delivery mode.
3
Configure the sender
Copy the webhook URL and paste it into the sender’s webhook settings.
If you have an admin role, you can copy the URL again from the webhook list.
503. You can find the broken webhooks in the Restate Cloud UI.
To invoke a renamed handler, create a new webhook and configure the sender with its URL.
Deleting a webhook stops its deliveries.
Requests usually stop reaching your handler within 30 seconds. If the Restate Cloud control plane is unavailable, this can take up to an hour.
Choose a delivery mode
Choose the delivery mode when you create the webhook. You cannot change it after creation. To switch modes, create a new webhook and update the sender’s URL.Write the handler
The examples on this page use the TypeScript SDK. Verify the sender’s signature before processing the request. Senders sign each request by computing a signature over the exact bytes of the body and including it in a header. To verify the signature, use the raw body and the request headers:- Declare the input as
restate.serde.binaryand setaccept: "*/*"to receive the unmodified request body as raw bytes, regardless of content type. - Read the headers from
ctx.request().headers. Header names are lowercase.
errorCode: 401.
Restate doesn’t retry the invocation, and in sync mode the sender receives 401.
Verify with the sender’s library
Many senders provide libraries for signature verification, for example@octokit/webhooks-methods for GitHub, LinearWebhookClient from @linear/sdk/webhooks for Linear, and isValidSlackRequest from @slack/bolt for Slack.
Pass the raw body and the signature header to the library in its required format. Linear’s client accepts raw bytes; Octokit accepts a string. For example, with Octokit:
TypeScript
Verify signatures manually
Without a library, compute the signature over the raw body with your signing secret, and compare it with the header in constant time. This handler receives GitHub pull request events, verifies their signatures, and forwards the events to a virtual object:TypeScript
Handle retries and slow work
Senders retry deliveries that fail or time out, and unless the sender sets anIdempotency-Key header (see below), each retry starts a new invocation of your handler.
In sync mode, senders may also retry if your handler exceeds their response time limit (see the table below).
To process each delivery once:
- Verify the request.
- Send the event to another handler, with an idempotency key derived from the sender’s delivery ID. Restate uses this key to deduplicate deliveries.
- Return immediately.
If a sender sets an
Idempotency-Key header, Restate deduplicates requests to the same webhook that carry the same key, so a retry doesn’t start a second invocation.
In async mode, the sender then receives "status": "PreviouslyAccepted".
How requests reach your handler
Incoming webhook requests must meet these requirements:POSTrequests only.- Bodies up to 1 MiB, with a
Content-Lengthheader. Chunked bodies without this header are rejected. - The exact path of the webhook URL. Requests with additional path segments return
404. - Up to 100 requests per second per webhook, with bursts of up to 200.
- The unmodified request body.
- The sender’s headers, available through
ctx.request().headerswith lowercase names. Restate Cloud removesAuthorization,Cookie, hop-by-hop headers such asConnectionandTransfer-Encoding, and headers that start withx-restate-orx-restatecloud-. - The webhook ID in an added
x-restatecloud-webhook-idheader. - The query string appended to the
x-restate-ingress-pathheader, excluding thedelayandlimit-keyparameters.
Configure webhook senders
GitHub
- In your repository or organization settings, add a webhook with the webhook URL as the Payload URL.
- Set Content type to
application/json. - Set a Secret and configure your service with the same value. The example above reads it from
GITHUB_WEBHOOK_SECRET.
2xx response.
GitHub signs each delivery in the x-hub-signature-256 header: sha256= followed by the hex HMAC-SHA256 of the body.
To retry a failed delivery, redeliver it from the webhook’s settings in GitHub; the redelivery keeps its x-github-delivery ID.
Linear
Use sync mode. Linear treats any response other than HTTP200 as a failure, including the 202 of async mode.
In Linear, create a webhook in your workspace’s API settings using the webhook URL, and configure your service with the webhook’s signing secret.
Linear signs each delivery in the linear-signature header, the hex HMAC-SHA256 of the body.
The webhookTimestamp field in the body is the time Linear sent the delivery, in milliseconds.
TypeScript
webhookTimestamp is more than a minute old.
Your handler may start after a delay, for example if your deployment was unavailable. Increase MAX_AGE_MS if you need to accept these delayed deliveries.
ctx.date.now() records the time on the first attempt, so a retry of the same invocation compares against the same time.
Slack
Use sync mode. Slack verifies an Events API request URL by sending a challenge that your handler must return, and it shows the response to a slash command as the reply. The example uses one handler per Slack request URL. Create two webhooks: one forevents, used as the Events API request URL, and one for command, used as the slash command’s request URL. To use a single handler for both, distinguish JSON events from form-encoded commands.
Slack signs each request in the x-slack-signature header: v0= followed by the hex HMAC-SHA256 of v0:<timestamp>:<body>, keyed with your app’s signing secret.
<timestamp> is the value of the x-slack-request-timestamp header. Slack recommends rejecting requests that are more than five minutes old.
TypeScript
eventsanswers theurl_verificationrequest by returning{ challenge }, and forwards message events with theirevent_idas the idempotency key.commandreceives the form-encoded slash command and returns a response. For processing that exceeds Slack’s response time limit, forward the command to another handler and post the result to the command’sresponse_urlwhen processing completes.
Limitations
- Senders that verify the URL with a
GETorPUTrequest are not supported because webhooks accept onlyPOST. - Bodies larger than 1 MiB are rejected, even if the sender supports larger payloads. GitHub, for example, sends payloads of up to 25 MB.
- A sync webhook waits at most 60 seconds for the handler, then returns
504. See Choose a delivery mode. - For senders that sign the full URL, such as Twilio and Square, provide the webhook URL through your service configuration to verify the signature. The webhook URL is not forwarded to your handler.
- Virtual objects and workflows cannot be webhook targets. Configure the webhook to invoke a service handler that forwards events to the object.