1. The big picture: two directions, one loop
The integrations platform has two halves that share one vocabulary.
Events out. Everything notable in OpsBrain is recorded as an operational event: a run completes, a worker flags a step, a bottleneck is detected. Notification rules decide which of those events reach people (Slack, Teams, SMS, Jira tickets), while webhook endpoints and Zapier deliver the full details to software.
Triggers in. Your ERP, WMS, CRM or automation tool sends an event to OpsBrain. A trigger rule turns it into a prefilled run waiting in a team's queue:
The two halves close the loop: when an externally triggered run finishes, its completion event goes back out through the same rules, webhooks and Zaps, including any differences between what your system said and what the worker actually found.
Try it in five minutes, no code
- 1Open Integrations from the sidebar and connect Slack. It's an approve-access screen, like signing into any app with Google. Pick a channel.
- 2Create your first notification rule: when a run completes, tell that channel. The builder previews the exact message before you save.
- 3Complete any run, or send a test from the rule, and watch the channel light up.
Curious about the inbound direction without involving IT? Create a source under Event Sources and use its Send test event button: OpsBrain plays the role of your external system, the event lands in the activity log, and you can see exactly what it would trigger without connecting anything.
2. Notification rules: WHEN / IF / THEN
A notification rule is a sentence: when this happens, if these conditions hold, then tell these places. You build every part by picking from lists; the only typing is values. The rule builder walks you through four labeled stages: WHEN (This happens), IF (It applies here, optional), THEN (Send it to), and FORMAT (Detail level). You can also Start from a preset for a curated starting point.
Scopes: organization, workflow, team
A rule's scope decides which slice of your operation it listens to: Entire organization, Specific workflows, or Specific teams. Pick the narrowest scope that says what you mean and you often won't need filters at all.
Filters
A filter is a condition on one field of the event's payload. Fields are addressed with dot paths (for example, the skipped-steps count inside the run part of the event), and all filters on a rule must pass. Filterable fields include Workflow, Workflow category, Team, Run status, Run duration (seconds), Skipped steps, Flags raised, Step is required, Flag type, Optimization type, User, and Member role. The operators cover equality (is / is not), list membership (in / not in), numeric comparison (greater than, at least, less than, at most), booleans (is true / is false), and case-insensitive text contains.
Filters fail closed: if the field doesn't exist on an event, or the operator doesn't apply, the condition fails and nothing is sent. A rule can never surprise you with more messages than you asked for.
Detail levels
Each destination renders the message at one of three sizes:
| Level | What arrives |
|---|---|
| Compact | The title only. For busy channels that just need a pulse. |
| Standard | Title plus a one-sentence summary of what happened. |
| Detailed | Title, summary, a block of facts, and a link back to the run. |
Each tool renders the message natively: Slack uses Block Kit with a button, Teams an Adaptive Card, Discord an embed. You choose the size of the words; the tool chooses the look.
Cooldowns (quiet periods)
Some events repeat fast. A worker who flags three steps in five minutes shouldn't page a channel three times. The Quiet period setting collapses repeats after a rule fires: Send every notification, At most one per 15 minutes, At most one per hour, At most one per 4 hours, or At most one per day. Collapsed messages aren't lost silently: each appears in the delivery log as a skipped delivery with its reason, so you can always prove why a message didn't arrive.
One rule, many destinations
A rule can deliver to several destinations at once, across different tools: the same event can post to a Slack channel at standard detail, create a Jira issue, and text the on-call phone. Each destination is delivered and tracked independently, so one failing tool never blocks the others. Prefer a handful of precise rules over one giant rule that fans out everywhere; precise rules are easier to mute and retarget later.
Testing a rule
- 1Use the Live preview in the builder. It renders your rule's message against a realistic sample of the chosen event.
- 2Save the rule and send a test delivery to its destinations. Test messages are clearly marked so nobody mistakes them for real activity.
- 3Check the Delivery log: every delivery, real or test, shows its status, attempts and timing there.
Rules can be paused at any time; a paused rule keeps its setup but sends nothing.
3. Connecting chat tools
You connect a tool once, add one or more channels as destinations, then point notification rules at those destinations. Connecting a tool by itself sends nothing; the rules decide what arrives.
Slack (OAuth)
- 1Go to Settings, then Integrations, then Slack, and press Connect.
- 2Approve the request in Slack. OpsBrain asks only for what it needs: sending messages and listing channels.
- 3Back in OpsBrain, add a channel destination. Public channels appear in the picker automatically.
The connection is workspace-wide, so other managers can add more channel destinations without reconnecting.
/invite, then add it as a destination.Microsoft Teams, Discord, and Google Chat (incoming webhooks)
These three use incoming webhooks: the channel or space gives you a URL, and anything posted to that URL appears as a message. No app registration, no admin console.
Teams:
- 1In Teams, open the channel and choose Workflows from the channel menu.
- 2Pick the template Post to a channel when a webhook request is received and confirm the channel.
- 3Copy the URL Teams generates, then add a Teams destination in OpsBrain and paste it.
Discord:
- 1Open Server Settings, then Integrations, then Webhooks, and press New Webhook.
- 2Name it, pick the channel, and press Copy Webhook URL.
- 3Add a Discord destination in OpsBrain and paste the URL.
Google Chat:
- 1Open the space, click its name, and choose Apps & integrations, then Webhooks.
- 2Add a webhook, name it OpsBrain, and copy the URL.
- 3Add a Google Chat destination in OpsBrain and paste it.
What the messages look like
OpsBrain composes one message and each tool renders it natively at the rule's detail level: Slack gets Block Kit with a button that opens the run, Teams an Adaptive Card, Discord a rich embed, Google Chat a card with a link button. Messages are written in your organization's language and always link back to the run or workflow they describe.
Troubleshooting, per tool
| Tool | Symptom | Fix |
|---|---|---|
| Slack | Connection expired; nothing posts. | The workspace token was revoked, often when the installing user left. Press Reconnect on the Slack page. |
| Slack | A private channel is missing from the picker, or messages to it fail. | Invite the bot inside Slack with /invite, then try again. |
| Teams | Deliveries fail after working for weeks. | The Workflows flow was deleted or its owner left. Recreate the webhook in Teams and paste the new URL into the destination. |
| Discord | Deliveries fail with a not-found error. | The webhook was deleted in Server Settings. Create a new one and update the destination. |
| Google Chat | Deliveries fail with a permission error. | The webhook was removed from the space, or the space was archived. Add a fresh webhook and update the destination. |
| Any tool | An expected message shows as skipped in the delivery log. | Read the reason on the delivery row: a cooldown collapsed a repeat, or the destination is paused. Both are deliberate and reversible. |
4. Work tracking and incident tools
Chat messages get read; issues and incidents get owned. Jira, Linear, ServiceNow, PagerDuty and Twilio are all rule destinations, exactly like a Slack channel: connect the tool once with its credentials, add a destination, and point rules at it. No code involved.
| Tool | You provide | OpsBrain creates |
|---|---|---|
| Jira | Site URL, account email, API token. | A Task issue in your chosen project, with the event details in the description. |
| Linear | A workspace API key. | An issue in your chosen Linear team. |
| ServiceNow | Instance URL, integration user, password. | A record in your chosen table. Incidents by default. |
| PagerDuty | An Events API v2 routing key per service. | An incident on that service, with severity mapped from the event. |
| Twilio | Account SID, auth token, a sending number. | A short SMS with a link to the run. |
Jira
Create an API token in your Atlassian account settings, then connect Jira in OpsBrain with your site URL (like your-company.atlassian.net), the account email, and the token. Add a destination by picking the project. A rule like "when a worker raises a flag, then create a Jira issue" produces a Task titled after the event, with the workflow, run, team and a link back to the run in the description.
Linear
Create an API key in Linear's workspace settings and paste it into OpsBrain; the key is verified against your workspace immediately. Add a destination by picking the Linear team. Typical use: route bottleneck detections and optimization suggestions to the team that owns process improvement, so every AI finding lands as a backlog item instead of a chat message that scrolls away.
ServiceNow
Connect with your instance URL and an integration user that can create records in the target table; the connection is verified live. Destinations target a table through the standard Table API: the incident table is set up by default, and you can point at any table your user can write to. Field values carry the event summary and a link back to OpsBrain, so your existing assignment rules and SLAs take over from there.
PagerDuty and severity mapping
PagerDuty needs no account-level connection: each destination is one service's Events API v2 routing key (in PagerDuty, open the service, add an Events API v2 integration, copy the key, paste it into a new destination). Severity is mapped from the event's category, so paging pressure matches operational urgency: execution exceptions (flags, skipped steps, escalations) arrive as error-severity alerts, while operational intelligence such as bottleneck detections and routine events arrive at lower severities.
Twilio SMS
SMS is the channel of last resort that always gets through: no app, no wifi, no problem. Connect Twilio with your Account SID, auth token, and sending number, then add destinations of one phone number each, in international format like +34 600 000 000. SMS messages are always compact regardless of the rule's detail level, with a link for the full picture.
5. Webhooks, for developers
A webhook flips the direction of asking: instead of your software polling OpsBrain for news, OpsBrain calls your software the moment something happens. Creating an endpoint takes two minutes in the app and no code; everything after that is for whoever writes the receiving code.
Create an endpoint
- 1In Integrations, under Webhooks, press New endpoint and name it after the consumer, like "Warehouse Operations API".
- 2Enter the destination URL. Production requires HTTPS.
- 3Pick the subscribed event types. You can change the list any time.
- 4Optionally scope the endpoint to specific teams or workflows, and add payload filters (for example, only when skipped steps are greater than zero). These are the same filters used by notification rules.
- 5Save, and copy the signing secret immediately. It is shown exactly once; if you lose it, rotate to get a new one.
"test": true, signed with your real secret, and shows you the HTTP status and response time immediately.The signed envelope
Every delivery is a POST with the same JSON body shape regardless of event type. The envelope carries: a unique event id (stable across retries, so it's your deduplication key), the event type (a stable, never-translated name like workflow.run.completed), a payload schema version (additive changes never bump it, breaking ones do), the UTC timestamp, a test flag, the organization, the actor (null for events OpsBrain raised itself, like bottleneck detections), and data, the event-specific payload. Headers repeat the event id, type and version, and add the signing timestamp (Unix seconds) and the HMAC signature; the user agent is always OpsBrain-Webhooks/1.0.
Verify the signature
The signature is an HMAC-SHA256 seal over the timestamp and the raw request body:
signature = v1=hex( HMAC_SHA256( "{timestamp}.{raw_body}", secret ) )Three checks, in order: recompute the HMAC and compare it constant-time against the header value after v1=; reject if the timestamp is more than 300 seconds from your clock (this stops replays); and hash the raw request bytes exactly as received, parsing the JSON only after the signature checks out. The four classic mistakes:
- Parsing then re-encoding the JSON before hashing. Key order or whitespace changes break the digest; always hash the raw bytes.
- Comparing signatures with plain string equality. That leaks timing information; use your language's constant-time comparison.
- Skipping the timestamp check. The signature alone does not stop a replay of a captured request.
- Sharing one secret across endpoints. Each endpoint has its own, so a leak only ever affects one consumer.
Retries, timeouts, and the degraded state
Respond with any 2xx within 10 seconds (the connection timeout is 5 seconds). Acknowledge first and do heavy work on your own queue. On 408, 425, 429 or any 5xx (and on timeouts), OpsBrain retries with backoff at 60 seconds, 5 minutes, 30 minutes and 60 minutes, up to 5 attempts, and a Retry-After header from you always wins. Any other 4xx is treated as permanent and not retried: your URL or auth is wrong; fix it and press Retry in the delivery log.
After 5 consecutive failed deliveries the endpoint is marked degraded. Nothing is turned off: deliveries keep being attempted, and the very next success restores it to active. The transition to degraded also raises an operational event, exactly once, so you can point a notification rule at it and hear about failing integrations in Slack without an alert storm. Because retries exist, the same event can arrive twice: deduplicate on the event id.
Delivery logs
Every delivery, attempt by attempt, lives in the Delivery log: each attempt records the HTTP status, latency, and the first 2000 characters of your response body, which is usually enough to see your own error message. Failed deliveries have a manual Retry button. Paused endpoints record skipped deliveries instead of receiving traffic, so a gap in your data is always explainable.
Rotating a secret
Rotation is instant and unforgiving, by design: press Rotate secret, and a new secret is generated and shown once. The old secret stops verifying at that same moment. Update your consumer immediately after copying, then send a test event to confirm the new secret verifies.
6. Zapier
Zapier is the no-code path out of OpsBrain: a Zap is a small recipe (when this happens, do that) that can reach thousands of apps. Connecting takes one key and about two minutes.
- 1In OpsBrain, open Settings, then Integrations, then Zapier, and press Connect Zapier. You get an API key, shown once. Copy it. The invite link for the OpsBrain app on Zapier is on the same page (Open OpsBrain on Zapier).
- 2In Zapier, add the OpsBrain app to a new Zap and paste the key when asked to sign in. The connection is labeled with your organization name.
- 3Turn the Zap on. OpsBrain starts sending matching events to it immediately. These are instant triggers: no polling delay.
Available triggers
Each trigger is a curated slice of the event stream: run completed, run started, run abandoned, run flagged, run handed off, step skipped, bottleneck detected, optimization generated, optimization accepted, optimization applied, workflow created, and workflow published. There is also the advanced firehose, New Operational Event, which fires on any event at all; add a Zapier Filter step on the type field to narrow it. Pick the specific trigger when it exists; it keeps the Zap editor simple.
What a Zap receives
Zapier's field mapper works poorly with nested JSON, so OpsBrain flattens the standard envelope before delivery: every field becomes a top-level token you can drag into any action. What lives at a nested path in a webhook becomes an underscore-joined name in Zapier (objects flatten one level). The samples Zapier shows while you build use exactly this shape, taken from your organization's recent real events when there are any.
Recipe ideas
- Run log in Google Sheets: trigger on run completed, action "Create Spreadsheet Row", map workflow, duration, skipped steps and worker to columns. A living log without anyone exporting anything.
- Flag raised, Jira ticket opened: trigger on run flagged, action "Create Issue", put the run link in the description. (OpsBrain also has a native Jira integration; the Zapier route makes sense when you want extra steps, like only during night shifts.)
- Externally triggered run, Slack DM to the supervisor: the firehose trigger plus a filter on the event type, then a Slack "Send Direct Message" action.
7. External triggers, part 1: event sources
An event source is the front door for one external system: your ERP, your WMS, a Zap, an n8n flow. Think of it as a named key you hand to that system. One source per system is the rule, not for technical reasons but because separate keys keep the story honest: the activity log shows which system sent every event, replacing a key affects one system at a time, and pausing your test ERP doesn't silence production.
Every source owns three things:
- An ingest URL, unique to the source:
https://<app>/api/ingest/{source-id}. - A bearer token starting with
obt_, which authenticates every request. It is shown exactly once. - Optionally a signing secret starting with
whsec_, for tamper-proofing requests (see below).
Creating one
- 1In Integrations, under Event Sources, press New source. Name it after the system (like "Dynamics ERP") and pick the kind: webhook, Zapier, n8n, or direct API. The wire contract is identical for all four; the kind only tailors the setup instructions you see next.
- 2Copy the bearer token. It is shown exactly this once. Store it like a password, wherever your team keeps shared secrets.
- 3Send your first event. The source page live-updates the moment it arrives, so you get a green confirmation the instant your system speaks.
The Send test event panel
Every source page has a Send test event button, the zero-setup way to try the whole inbound flow: OpsBrain acts like your external system for you. The panel lets you set the Event name (for example shipment.received) and build the Event data field by field, one row per field (numbers are sent as numbers), or switch to Edit as JSON. The Also run my triggers toggle controls what happens: off, the event is just stored so you can map its fields; on, OpsBrain treats it like a real event and creates runs if a trigger matches. Either way the event lands in your activity log like real traffic.
Sending events (the contract, in brief)
POST /api/ingest/{source}
Authorization: Bearer obt_…
Content-Type: application/json
{
"event": "shipment.received",
"data": {
"shipment_id": "SH-10482",
"supplier": { "name": "ABC Packaging" },
"expected_pallets": 12
}
}The body has two fields, and that's the entire schema. event is your name for what happened: any string up to 200 characters, though lowercase dot-separated names in past tense keep your trigger list readable. Event names are machine identifiers: never translated, and never changed once triggers depend on them. data is any JSON object up to 256 KB; nest it however your domain looks, and triggers address fields with dot paths like supplier.name.
The response is 202 Accepted, which means "stored", not "matched": matching and run creation happen asynchronously, and the inbound activity log shows a moment later what each event matched and which runs it created. Adding "test": true to the body stores the event without creating runs. For exactly-once delivery, send a unique X-OpsBrain-Idempotency-Key header per logical event: replays of the same key are acknowledged with 200 {"duplicate": true} without storing anything twice.
Optional request signing
A source can require every request to carry an HMAC signature over the timestamp and raw body, Stripe-style, with a 5-minute tolerance:
X-OpsBrain-Signature: t=1751730000,v1=hex(hmac_sha256("{t}.{raw_body}", secret))The scheme is byte-for-byte the one OpsBrain uses for outbound webhooks, symmetric on purpose: the same code and test vector verify both directions. Turn signing on for production ERP and WMS integrations, where a leaked token alone should not be enough to inject events. Skip it for Zapier and n8n, which can't easily compute request signatures; the bearer token plus HTTPS is the standard bar there.
Pausing
Pausing a source politely turns requests away with a 409 response without dropping any configuration: the token, the signing secret and every trigger that references the source stay intact. Resume and events flow again. Use it during ERP maintenance windows, or to silence a misbehaving system while you investigate.
Connecting your system, easiest first
| Kind | Setup |
|---|---|
| Zapier | No code. "Webhooks by Zapier" action: POST, the ingest URL, an Authorization Bearer header with your token, and a body built by dragging in the Zap's trigger fields. |
| n8n | No code. HTTP Request node: POST, the ingest URL, a Header Auth credential holding the bearer token, and a body mapped from upstream nodes. |
| Webhook (ERP / WMS / CRM) | Configuration, not code. Most systems have a webhook or outbound-message facility: point it at the ingest URL and add the Authorization header. If it can't set headers, route it through Zapier, n8n or Make and forward from there. |
| Direct API | For developers. Call it from your own backend like any HTTP API. |
Limits and retention
- Rate limit: 120 requests per minute per source. Beyond that, HTTP 429; retry with backoff.
- Payload cap: 256 KB per request body (HTTP 413 beyond it). Send identifiers, not documents: a trigger needs the shipment id, not the scanned invoice.
- Retention: the inbound event log is pruned after 30 days. Runs keep their own copy of the mapped data forever, so nothing a run was built from ever disappears.
8. External triggers, part 2: run triggers
A run trigger is the bridge between an event your system sends and work a person does: when this event arrives, if its details match, create this run, prefilled with this data, in this team's queue. Triggers live in Integrations, under Run Triggers, and the builder walks you through four stages: WHEN (an event arrives), IF (only when the payload matches, optional), THEN (create a run), and MAP (map fields onto steps).
WHEN: event name and source scoping
The Event name is an exact, case-sensitive match against the event field your system sends. Two more knobs narrow it down: source scoping (by default a trigger listens to Any source; scope it to one source and a staging event will never create production runs) and multiple triggers per event (several triggers can listen to the same event name and each creates its own run, so a damaged shipment can spawn both an inspection run and a supplier claim run).
IF: payload filters
Conditions on the payload, all of which must hold: expected_pallets greater than 0, status equals damaged, and so on. Paths address nested fields with dots. Filters fail closed: a missing field means no match, so a malformed event can never create a half-configured run. The operators are the same ones used by notification rules.
THEN: the run that gets created
- Workflow and team: pick any live workflow and the team whose queue receives the run (or the workflow's own teams).
- Priority: Low, Normal, High, or Critical, which drives the queue's default ordering. Damaged-goods triggers create critical runs; routine cycle counts create low ones.
- Run name template: what the team sees in the queue. Tokens are payload paths in double curly braces, so "Receiving Inspection {{shipment_id}}" reads like work, not like "Run #4187". Unresolved tokens stay visibly raw instead of disappearing, so a wrong path is easy to spot.
- External reference template: your system's id for the work, carried on the run and echoed back in every event about it, so your ERP can match completions to its own records.
- Dedupe template: if a new event renders the same dedupe key as an open queued run from the same trigger, no second run is created. ERPs that fire twice stop being your problem.
- Claim SLA (minutes): if nobody claims the run in time it is escalated: it raises an escalation event and sorts to the top of the queue. Escalation adds urgency; it never reassigns. Point a notification rule at the event to ping managers in Slack or by SMS.
Republish safety: needs review
Mappings point at specific workflow steps, and workflows evolve. When a workflow edit removes or moves a mapped step, the trigger is flagged Needs review instead of silently breaking or guessing: it stops creating runs and is clearly marked on the triggers page. Open it and the remap flow shows each orphaned mapping next to the workflow's current steps; point each at the right step or drop it, and save. The trigger resumes immediately. Events that arrive while a trigger needs review are stored and marked unmatched, so you can see exactly what was missed.
9. The visual field mapper
Mappings are the prefill: each one connects a payload field to one input step of the workflow, so the worker starts with your system's data already in place. Four step types can be prefilled: short text, long text, number, and date. You never have to type payload paths from memory; the mapper works from a sample payload, and there are several ways to get one:
- Send a sample from your system: POST one representative event per event name to
/api/ingest/{source}/sample(same auth and body shape as the real endpoint). Samples are stored for the mapper and never create runs. - Use last received event: pull the sample straight from real traffic the source already received.
- Paste sample JSON: paste a payload by hand (it must be a JSON object with an "event" name and a "data" object).
- The source's Send test event panel, described in section 7.
- Generate fields from workflow: no sample at all? OpsBrain works backwards, generating a field for each of the workflow's prefillable steps. The generated fields are fully editable.
With a sample in place, the builder shows your Detected fields with their values and types (text, number, date, yes/no) next to the workflow's prefillable steps, previews every template against them, and offers Suggested mappings scored with a percent match. Accept or dismiss each suggestion; it's a starting point, not magic. Add more rows with Add a mapping; each row picks a Payload field, a Workflow step, and a Behavior.
Renaming fields and sharing the contract
Hover any detected field and use the pencil (Rename field) to give it the name you want your system to send. The rename applies everywhere at once: the sample, the mappings and the templates update together, so the contract stays consistent. When the fields look right, press Copy example payload and hand the copied JSON to the team that will configure the sending system; it is exactly what OpsBrain expects to receive.
The three mapping modes
| Mode | What the worker sees |
|---|---|
| Editable | The field arrives filled in but behaves like any input: the worker can correct it and move on. If they change it, the difference is recorded as a deviation. The default. |
| Read-only | The value is displayed but cannot be changed, and the server rejects any attempt, not just the UI. For facts the worker should see but never touch, like the supplier name. |
| Confirm | The value is filled in, but the step will not complete until the worker explicitly confirms it, either as-is or after correcting it. For numbers you want a human to actually check, like the pallet count. |
In the run player, prefilled fields carry a chip naming the source system, read-only values render as a value card with an explanation, and confirm steps change the button to Confirm and continue with the hint to check the value matches reality.
10. The team queue: claim, release, escalate, deviations
When a trigger creates a run, no person owns it yet. It waits in the Queue tab of Active Runs until someone claims it. Everyone on the team sees the same queue; there is no hidden assignment.
- Each entry shows the run name from the trigger's template, a priority chip, a source chip (External or the named source system, versus Handoff for cross-team handoffs), and how long it has been waiting.
- Stat tiles summarize the queue: Awaiting claim, Escalated, and Longest wait.
- Escalated runs are visually flagged and sort to the top. The queue orders by priority (critical first) and then by oldest first.
- Buttons per card: Claim run and View run.
Claiming
Pressing Claim run does two things: it moves the run out of the queue so nobody else starts it, and it opens the guided run for you, prefilled with the data the external system sent. The claim is recorded (who, when, and how long the run had waited), and for externally triggered runs the human clock starts at claim, not at trigger time, so wait time in the queue never inflates the worker's duration. Claiming raises its own operational event, so an ERP that wants to show "inspection in progress, claimed by Ana" just subscribes to it.
Releasing and reassigning
Claimed the wrong run, or got pulled away? Return to queue removes your claim and puts the run back for anyone eligible to take, with its prefilled data intact. Releasing is for runs you haven't really started: once any step is completed, release is blocked, because recorded step data belongs to whoever recorded it. If a run truly must change hands mid-way, a manager uses Reassign to hand it to a teammate explicitly; the reassignment is recorded and the new owner picks up exactly where the run stands. Use it for shift handovers and sick days rather than sharing logins.
Escalation
Driven by each trigger's claim SLA and checked every minute: an unclaimed run past its SLA is marked escalated, sorts to the top, and raises an event your notification rules can turn into a Slack ping or an SMS. Escalation never auto-assigns; it makes the wait loud and leaves the decision with people. The run stays claimable the whole time.
Deviations: what your system said vs what was found
The ERP said 12 pallets. The worker counted 11. That difference is a deviation, and OpsBrain treats it as a first-class result, not a data-entry correction. A deviation is recorded whenever a worker changes a prefilled editable value or corrects a confirm value, keeping the step, the payload path, the expected value and the actual value. Deviations appear in three places: on the run's detail page as a readable comparison, in the completion event's payload so the source system learns the truth without re-typing, and in OpsBrain's observation layer, where recurring deviations (a supplier that consistently ships short) become patterns worth acting on.
Completion, back to your systems
Every queue lifecycle moment is an ordinary operational event, so your existing rules, webhook endpoints and Zaps subscribe to them like anything else:
| Event | When |
|---|---|
integration.event.received / .unmatched | An inbound event was accepted / matched no trigger. |
workflow.run.triggered | A trigger created a queued run. |
workflow.run.claimed | A team member claimed it. |
workflow.run.released | It went back to the queue. |
workflow.run.escalated | Nobody claimed it within the SLA. |
workflow.run.completed | The guided run finished. |
For externally triggered runs, the completion event additionally carries the trigger context, the external reference, and the deviations, closing the loop with the system that started the work:
{
"trigger": { "id": "…", "event_name": "shipment.received", "source": "ERP" },
"deviations": [
{ "step": "Expected pallets", "source_path": "expected_pallets",
"expected": "12", "actual": "11" }
]
}11. The inbound activity log
Under Integrations, the Inbound activity page lists every event external systems pushed in, what it matched, and the runs it created, with columns Event, Status, Triggers, Runs, Received, and Source, plus a search by event name. Unmatched events and test events are stored right alongside real traffic, so nothing that arrived is ever invisible.
- Inspect opens the full detail: the raw payload, the idempotency key, and the runs the event created.
- Replay runs the trigger engine again on the stored event. This is how you recover events that arrived before a trigger existed, or while a trigger was flagged needs review. The confirmation notes the safety net: triggers with a dedupe template will not create duplicate runs on replay.