This guide shows how to design a webhook integration that can receive business events, store processing status, send the data to another system, and retry failed exports without losing track of what happened.
Use this pattern when an external system sends orders, sales, bookings, call events, form submissions, or similar records to Bosbec and another system needs to receive a transformed version of the same data.
The guide combines the API pattern from Building Your First API, the Unit creation pattern from POST Units to HTTP-in, and the debugging approach from Troubleshooting your workflow. It is not tied to a specific external service.
What you will build
One workflow with three practical parts:
| Part | Purpose |
|---|---|
POST /webhook |
Receives events from the source system |
| Event Unit | Stores the original payload, status, attempts, and last error |
| Retry trigger | Finds failed events and tries to send them again |
The source system gets a clear HTTP response from Bosbec. The destination system gets the transformed request. Your team gets a status record that can be searched, inspected, and retried.
Before you start
- Create an HTTP-in channel for the incoming webhook.
- Create an API token if you want to test the endpoint outside the source system.
- Create a group named
Webhook Events, or another name that matches your business process. - Copy the group ID and store it in Account Settings as
webhook_events_group_id. - Read POST Units to HTTP-in if you are new to creating Units from incoming JSON.
There is no dedicated template for this pattern. Build it from the jobs described below and adapt the field names to your own event type.
Design the event record
Store one Unit for each incoming event. The Unit is the processing record, not only the business object.
Use metadata similar to this:
| Metadata key | Example | Purpose |
|---|---|---|
external_id |
ORD-10042 |
Stable ID from the source system |
event_name |
order_created |
Helps route different kinds of events |
status |
received, sent, failed |
Current processing state |
attempts |
0, 1, 2 |
Number of export attempts |
received_at |
2026-09-17T10:15:00Z |
When Bosbec received the event |
last_attempt_at |
2026-09-17T10:16:00Z |
When the latest export was tried |
last_error |
401 Unauthorized |
Last destination error, if any |
payload |
JSON string | Original webhook payload for inspection |
The external_id is important. Without a stable ID, retries and duplicate detection become guesswork.
Receive the webhook
Create an Incoming HTTP trigger on your HTTP-in channel.
Configure it as:
| Setting | Value |
|---|---|
| Method | POST |
| Path | webhook |
Add a Parse JSON to Resource job and parse:
{{incoming_http_request.body}}
Save the parsed resource as request_data.
The incoming payload will depend on the source system. For an order event, it might look like this:
{
"id": "ORD-10042",
"event_name": "order_created",
"created_at": "2026-09-17T10:15:00Z",
"customer": {
"email": "customer@example.com"
},
"total": 1295
}
Some systems call this field event, topic, type, or something else. The important part is that you choose one field that tells the workflow what kind of event was received.
Prevent duplicate events
Before creating a new Unit, add a Unit Pipeline that searches the Webhook Events group for an existing Unit where metadata.external_id matches the incoming event ID.
Save the result as existing_event and add a route:
| Check | Continue when |
|---|---|
{{existing_event.count()}} |
0 |
If a matching Unit already exists, return 200 OK or 409 Conflict, depending on what the source system expects.
For most webhook senders, 200 OK is better for duplicates that have already been accepted. It tells the source system not to keep retrying an event Bosbec already knows about.
{
"status": "duplicate",
"external_id": "{{request_data.id}}"
}
Create the processing Unit
Add a Unit Pipeline that creates one Unit from request_data and writes it to the group ID stored in webhook_events_group_id.
Map the fields you need for searching and status tracking:
| Unit field | Value |
|---|---|
metadata.external_id |
{{request_data.id}} |
metadata.event_name |
{{request_data.event_name}} |
metadata.status |
received |
metadata.attempts |
0 |
metadata.received_at |
{{request_data.created_at}} |
metadata.payload |
The original request body |
After the mapping, add these Unit Pipeline steps in order:
- Save the created Unit as a resource named
event_unit. - Save the Unit to the account.
The first step makes the Unit available later in the workflow context. The second step persists it on the Bosbec account, which is what makes the event searchable and retryable after the workflow run has finished.
Respond to the source system after the event has been stored. This confirms that Bosbec has accepted responsibility for processing it.
{
"status": "received",
"external_id": "{{event_unit.metadata.external_id}}",
"event_id": "{{event_unit.id}}"
}
Use 202 Accepted if processing continues after the response. Use 200 OK if the source system does not handle 202 well.
Send the event to the destination system
After storing the Unit, transform the incoming event into the structure required by the destination system.
For example:
| Incoming field | Destination field |
|---|---|
id |
externalOrderId |
customer.email |
customerEmail |
total |
amount |
created_at |
orderedAt |
Use a JSON Pipeline or Data operations job to build the outgoing body, then use Send HTTP Request to call the destination API.
If the destination request succeeds, update the Unit:
| Metadata key | Value |
|---|---|
status |
sent |
attempts |
Increase by 1 |
last_attempt_at |
Current attempt time |
last_error |
Empty value |
If the destination request fails, update the Unit instead:
| Metadata key | Value |
|---|---|
status |
failed |
attempts |
Increase by 1 |
last_attempt_at |
Current attempt time |
last_error |
Status code or error message from the destination request |
This makes the integration inspectable even when the external API is temporarily unavailable.
When you update these status fields, save the updated Unit to the account as the final Unit Pipeline step. Saving it only as a resource updates the workflow context, but the status change will not be persisted after the run.
Add a retry trigger
Create a scheduled or manual trigger that searches the Webhook Events group for Units where:
| Field | Value |
|---|---|
metadata.status |
failed |
metadata.attempts |
Less than your retry limit |
Loop over the matching Units with For Each Resource. For each Unit, rebuild the outgoing request from the stored payload or mapped metadata, send it to the destination system again, and update the same status fields.
A common retry limit is three to five attempts. After that, keep the Unit as failed and let a person inspect the error before trying again.
If your destination API uses idempotency keys, send metadata.external_id as that key. This helps the destination system ignore duplicate requests if the first attempt succeeded but the response was lost.
Test it
Test the workflow one layer at a time.
- Save and activate the workflow.
- Send a valid webhook request and confirm that Bosbec returns
received. - Open the
Webhook Eventsgroup and confirm that a Unit was created. - Send the same request again and confirm that the duplicate route is used.
- Temporarily configure the destination URL or token incorrectly and confirm that the Unit becomes
failed. - Fix the destination configuration and run the retry trigger.
- Confirm that the same Unit changes from
failedtosent.
Useful status responses:
| Status | When to use it |
|---|---|
200 OK |
The event was accepted or was already known |
202 Accepted |
The event was stored and will be processed asynchronously |
400 Bad Request |
Required event fields are missing |
401 Unauthorized |
Authentication failed |
409 Conflict |
A duplicate should be reported as a conflict |
500 Internal Server Error |
Bosbec could not store or process the event |
Troubleshoot the integration
Start with the incoming side.
If no Unit is created, open Workflow Starts and confirm that the webhook trigger started. If there is no start, check that the workflow is active, the channel is selected, and the source system is sending to the correct URL.
If the workflow started but the Unit is missing, open the run in interactive mode and inspect the parse, duplicate check, and Unit Pipeline jobs.
If the Unit exists but the destination did not receive anything, inspect the Send HTTP Request job, including URL, headers, body, and authentication. Store the response status or error body in metadata.last_error so the next person does not need to reconstruct the failure from memory.
For a broader checklist, see Troubleshooting your workflow.
Extend the solution
- Store raw events in one group and normalized business records in another group.
- Add a separate
dead_letterstatus for events that reached the retry limit. - Add a small internal page using Hello World Interface to search failed events and trigger manual retries.
- Add alerting when a certain number of events fail within a short period.
- Reuse the same pattern for orders, invoices, sales, call logs, contact imports, or support tickets.
Where to go next
This pattern is useful whenever Bosbec sits between two systems and you need more reliability than a direct pass-through request.
Next, adapt one of these guides using the same status-tracking approach:
- Integrating Sales with Personalkollen
- Build an AI-Queryable Order API
- Using MCP to Query Data Stored in Your Bosbec Account