orbi

Outgoing connections (webhooks)

Tell your own systems what happens in Orbi. Twenty-six events, narrowing by board and collection, signature verification, a delivery log and manual resend.

An outgoing connection sends a message to an address you choose whenever something happens in Orbi. When a task is opened, a record is written to a collection or a run finishes, your own system — your CRM, your chat tool, your automation platform — learns about it immediately and does its own work.

This is the outbound direction. If you want an event from outside to start a flow inside Orbi, see the inbound trigger on the Flows page.

Adding a connection

Go to Settings › Integrations and use Add webhook. Only admins can do this: an address that opens outwards is a decision for the whole workspace.

You set four things:

  1. Address: the https:// address the request goes to.
  2. Events: which events are sent. Pick as many as you like.
  3. Narrowing: optional. You can send only the tasks of certain boards, only the records of certain collections, or only changes made by people (or only by agents). If no box is ticked, everything is sent. File events are not narrowed by the board filter, because a file belongs to a space, not to a board; spaceId in the body tells the file's space.
  4. Closed spaces: optional. Events from Genel and the open spaces are always sent. Events from a closed space go out only when it is ticked in this list. The list shows only the closed spaces you are a member of. A space someone else ticked that you cannot see stays ticked when you save. Opening a closed space to a webhook is written to the Audit tab.

The moment you save, a secret is shown. It is never shown again, so save it. You will use it to verify that a request really came from Orbi.

Which events are there?

Group Events
Tasks task.created · task.state_changed · task.assigned · task.updated · task.deleted · task.comment_created · task.checklist_item_changed · task.attachment_added
Collections record.created · record.updated · record.deleted
Files file.created · file.version_added
Runs agent_run.started · agent_run.completed · flow_run.started · flow_run.completed
Membership member.joined · member.left · member.role_changed
Credit credits.low · credits.exhausted

Four events stay in the list under their old names: task.done, task.review, task.error and schedule_job.completed. These are names from the first release and they keep working — an integration you built does not go quiet because we renamed something. If you are setting up a new connection, task.state_changed and flow_run.completed carry the same events with more information.

The body you receive

Every request carries the same envelope; only the data field changes per event.

JSON
{
  "id": "9f1c…",
  "type": "task.state_changed",
  "version": 1,
  "occurredAt": "2026-09-04T09:12:03.000Z",
  "workspaceId": "…",
  "actor": { "type": "user", "id": "…", "name": "İbrahim" },
  "data": {
    "taskId": "…",
    "taskNumber": 128,
    "title": "Error on the invoice screen",
    "state": "review",
    "previousState": "in_progress",
    "projectId": "…",
    "projectName": "Product"
  }
}

actor says who did it: a person (user), an agent (agent), an outside system (external) or Orbi itself (system).

If the Orbi process driving a card's run shuts down midway, another process takes the run over and the work goes on where it stopped. You then receive two events: agent_run.completed carries the old run's runId with "status": "taken_over", and the agent_run.started that follows carries the old run's id in resumedFromRunId. Use that field to link the two runs.

These headers come with it:

Header What it carries
X-Orbi-Event The event name
X-Orbi-Event-Id The event's id — use this so you never process one event twice
X-Orbi-Delivery The id of this delivery attempt
X-Orbi-Signature The signature
X-Orbi-Timestamp When the signature was produced (seconds)

New fields may be added to the body; that is not a breaking change, so ignore fields you do not recognise. If a field ever has to be removed or change meaning, version goes up and the old version keeps being sent for a while.

Verifying the signature

The signature is produced from the text timestamp.body with your secret. On your side:

JS
const crypto = require('crypto');

function verify(req, secret) {
  const timestamp = req.headers['x-orbi-timestamp'];
  const signature = req.headers['x-orbi-signature'];
  // The raw body is required: a body parsed into JSON and serialised again
  // breaks the signature (field order and whitespace change).
  const raw = req.rawBody.toString('utf8');

  // Replay window: a request older than five minutes is rejected.
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.${raw}`).digest('hex');
  // Several signatures may arrive comma-separated (the secret rotation window).
  return signature.split(',').some((value) =>
    crypto.timingSafeEqual(Buffer.from(value.trim()), Buffer.from(`sha256=${expected}`)),
  );
}

Two things matter: sign the raw body, and reject the request if the timestamp is old. Without the second one, an old recorded request could be replayed against you.

Changing the secret

Click the endpoint and Rotate secret in the panel that opens produces a new secret and shows it once. The old secret stays valid for 24 more hours, and during that window every request arrives with two signatures — so you can update your receiving system without rushing. After the window the old signature is no longer sent.

The delivery log

In the endpoint list each row is one address: how many events it subscribes to and how it is narrowed are written on the row. Click the row and the endpoint's panel opens; the subscribed events, the delivery log and every action (edit, send test, rotate secret, pause, delete) live there.

The panel's Delivery log shows recent deliveries: status, which attempt it was, the HTTP code the receiver returned and how long the request took. Details shows the exact body that was sent and the exact response — the answer to "why didn't it arrive" usually sits right there.

If the receiver does not return 2xx, the request is retried three more times (after 1 minute, 5 minutes and 30 minutes). If all of them fail, the delivery stays failed and you can send it by hand: Resend for one row, Resend failed ones for all of them. A resent request is the same event — the event id does not change, so deduplication on your side keeps working.

Send test sends a sample request of type orbi.test. It does not carry a real event name, because a test request should not make your automation think something actually happened.

If a connection stops

If five deliveries fail in a row, the connection is paused automatically and the admins are notified. This is not a deletion: the events and narrowing you chose stay in place. Once the address is fixed, Turn back on picks up where it left off; the counter resets on the first successful delivery.

Without the pause we would keep sending requests to a dead address for hours and you would never find out.

Limits

  • At most 60 requests per minute go to one address. If a bulk operation produces more events, the rest move to the next minute — they are not lost.
  • Internal network addresses (localhost, private IP ranges) are not accepted.
  • Redirects (3xx) are not followed; write the final address.
  • A request that gets no answer within 10 seconds counts as failed.

For the full list of events and other system limits, see the Quick reference page.