Skip to content

Integrations & Webhooks

Webhooks let an external system react to what happens in produuz.it. When an event you subscribed to occurs, produuz.it sends an HTTP POST to your URL with a JSON body you choose.

How webhooks work

  1. You create a webhook in Company → Automations → Webhooks, picking the events it reacts to and the URL to call.
  2. You pick which blocks of data go in the body (Payload content).
  3. When one of those events happens, produuz.it builds the body and posts it to your URL.

One webhook can hold several events. If the same endpoint should hear about three of them, pick all three in the same webhook instead of creating three rows with the same URL, secret and headers. Every request carries "event", so your endpoint always knows which one it is.

The number of webhooks you can have is limited by your licence, and the counter is shown above the table (3/10 Webhooks). It counts rows, not events, so grouping events into one webhook uses less of your licence.

What your endpoint receives

  • Method POST, header Content-Type: application/json.
  • The body always contains "event" with the event name, plus whatever payload blocks you enabled.
  • produuz.it waits up to 5 seconds to connect and 10 seconds for your answer.
  • Any 2xx/4xx/5xx is accepted as an answer; only 200 is treated as success.
  • There is no retry. If your endpoint is down, times out or errors, that notification is lost and nothing is re-sent. Every attempt is recorded, so you can see it happened — see Delivery log.

Calls are made by a background worker, not by the request that raised the event, so a slow endpoint of yours never slows down the tablet on the shop floor. Two consequences for your side:

  • Expect the call a few seconds after the event, not at the same instant.
  • Order is not guaranteed. Two events raised close together can arrive in either order. Use the timestamp in serialized_event if the sequence matters to you.

Events

EventTriggered when
constructible_startAn operator starts a panel or module in the production viewer (the panel checklist is opened and production begins).
constructible_readyA panel or module completes its last station, or is marked ready in bulk from the constructibles list.
station_startAn operator starts one station for a panel or module. Also raised for each earlier station that is filled in when a panel is started at a later station.
station_readyAn operator marks one station as finished for a panel or module. Also raised for each earlier station filled in the same way.
part_batch_startA part batch is started in the logistics viewer.
part_batch_readyA part batch is finished in the logistics viewer.
report_deprecationA new IFC is detected for a design and its existing reports are marked obsolete.
rack_load_readyEvery panel of a rack load is marked in rack.
rack_load_deliveredEvery panel of a rack load is marked delivered.

unknown and mounted rack statuses raise nothing: the first is an undo, the second happens on site.

The station events are the loudest of the list: one per station and per panel, so a line with six stations sends six times what constructible_ready sends. Starting a panel at a later station also fills in the earlier ones, and each of those raises its own pair. Subscribe to them when you track station occupancy; use constructible_start and constructible_ready when you only track panels.

Configuring a webhook

FieldDescription
NameFree text, up to 80 characters. Only used to recognise the row in the table.
EventsWhich events trigger this webhook. Pick one or several.
Fire on every production lineFires whatever the production line, including lines created later. Disables the list below.
Production linesThe production lines this webhook fires on. Select at least one: an empty list means the webhook never fires.
Payload contentWhich blocks of data to include in the body. See Payload content.
Auth header typeNone, Secret or Bearer. Adds an Authorization header.
SecretThe token sent after the auth header type. Only shown when a type is selected.
URLThe endpoint that receives the POST. Must be http:// or https://.
Custom headersExtra HTTP headers, as name/value pairs.
Custom payload fieldsExtra name/value pairs merged into the body.

Production lines

A webhook only fires on the production lines you select. Selecting none is not a shortcut for "all lines" — the webhook stays silent, and the form warns you about it.

If you want a webhook to fire everywhere, turn on Fire on every production line. That covers the lines you have today and any line you create later, so you do not have to come back and edit the webhook each time. With the switch off, a new production line is not added to your webhooks automatically: add it yourself to each webhook that should cover it.

Three events never belong to a production line: report_deprecation, rack_load_ready and rack_load_delivered. They reach every webhook subscribed to them, whatever lines it lists, and a webhook holding only those does not need a line at all — the form does not warn about it. An event on a batch with no line assigned is treated the same way.

Payload content

Payload content is a multiple choice. Each option adds one block to the JSON body. By default only serialized_event is selected.

OptionAdds
serialized_eventThe production event itself, merged at the top level of the body.
serialized_stationproduction_station: the station the event happened at.
serialized_constructibleassembly and/or module: the panel or module the event is about.
serialized_part_batchpart_batch: the batch, its session proof and the sizes it covers.
employees__client_idsemployees__client_ids: your own ids for the people the event belongs to.
serialized_employeesemployees__details: the same people as full rows, with name and alias.
nicelabel_printA NiceLabel print job, merged at the top level. See the NiceLabel tutorial.

A block that cannot be built is simply left out — a station payload on an event with no station, for example. The request is still sent.

serialized_event

The production event, merged at the top level. The account the event was posted from is deliberately not included.

json
{
  "event": "station_ready",
  "uuid": "4b0a3f2e-6c1d-4a1f-9c2b-9f9b1f0f2a11",
  "timestamp": "2026-09-14T08:41:12.884Z",
  "project": "9d1e7b6a-2f44-4f0a-9a1c-1b2c3d4e5f60",
  "assembly": "e8f7e7f7-3b27-41e0-bb78-99eae64ba7cd",
  "module": null,
  "part_batch": null,
  "assembly_design": 45213,
  "module_design": null,
  "report": null,
  "employees": [312, 318],
  "employee_aliases": ["JAN", "PIET"],
  "meta": {
    "frontend_timestamp": "2026-09-14T08:41:12.640Z",
    "station_name": "Nailing",
    "station": "7a2c8e11-9d3b-4c22-8f1e-0b5a6d7c8e90"
  }
}
{
  "event": "station_ready",
  "uuid": "4b0a3f2e-6c1d-4a1f-9c2b-9f9b1f0f2a11",
  "timestamp": "2026-09-14T08:41:12.884Z",
  "project": "9d1e7b6a-2f44-4f0a-9a1c-1b2c3d4e5f60",
  "assembly": "e8f7e7f7-3b27-41e0-bb78-99eae64ba7cd",
  "module": null,
  "part_batch": null,
  "assembly_design": 45213,
  "module_design": null,
  "report": null,
  "employees": [312, 318],
  "employee_aliases": ["JAN", "PIET"],
  "meta": {
    "frontend_timestamp": "2026-09-14T08:41:12.640Z",
    "station_name": "Nailing",
    "station": "7a2c8e11-9d3b-4c22-8f1e-0b5a6d7c8e90"
  }
}

employee_aliases is a snapshot taken when the event was recorded, so renaming or removing someone later does not rewrite history. employees holds the matching account ids.

serialized_station

Resolved from the station uuid in the event's meta, so it is only present on events that carry one: station_start and station_ready.

json
{
  "production_station": {
    "name": "Nailing",
    "uuid": "7a2c8e11-9d3b-4c22-8f1e-0b5a6d7c8e90",
    "production_line_name": "Line 1",
    "production_line_uuid": "1c2d3e4f-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
    "client_station_id": "ST-02"
  }
}
{
  "production_station": {
    "name": "Nailing",
    "uuid": "7a2c8e11-9d3b-4c22-8f1e-0b5a6d7c8e90",
    "production_line_name": "Line 1",
    "production_line_uuid": "1c2d3e4f-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
    "client_station_id": "ST-02"
  }
}

serialized_constructible

Adds assembly, module, or both, depending on what the event points at. Both blocks have the same shape.

json
{
  "assembly": {
    "uuid": "e8f7e7f7-3b27-41e0-bb78-99eae64ba7cd",
    "design_uuid": "db0cb2a6-8fbf-46ac-81f5-ece6faca584e",
    "design_name": "ML-0B-5",
    "status": "working",
    "scheduled_prod_date": "2026-09-15",
    "client_order_id": "1030622",
    "batch_uuid": "e8986e12-4454-4ae5-a40d-4929d6a5b789",
    "batch_name": "Batch 1",
    "phase": "Phase_1"
  }
}
{
  "assembly": {
    "uuid": "e8f7e7f7-3b27-41e0-bb78-99eae64ba7cd",
    "design_uuid": "db0cb2a6-8fbf-46ac-81f5-ece6faca584e",
    "design_name": "ML-0B-5",
    "status": "working",
    "scheduled_prod_date": "2026-09-15",
    "client_order_id": "1030622",
    "batch_uuid": "e8986e12-4454-4ae5-a40d-4929d6a5b789",
    "batch_name": "Batch 1",
    "phase": "Phase_1"
  }
}

serialized_part_batch

Only present on part_batch_start and part_batch_ready. It carries the batch, the proof of the session (times, pauses, the parts marked ready, the material declared as returned) and the sizes the batch covers, so you never have to walk the parts yourself.

json
{
  "part_batch": {
    "uuid": "b1f0c2d3-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
    "name": "PB-2026-014",
    "status": "working",
    "priority": 0,
    "scheduled_prod_date": "2026-09-15",
    "production_line": "Saw line",
    "production_line_uuid": "1c2d3e4f-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
    "project_name": "Vianen",
    "project_uuid": "9d1e7b6a-2f44-4f0a-9a1c-1b2c3d4e5f60",
    "parts_count": 412,
    "ready_count": 0,
    "sizes": [
      {
        "branch": "timber",
        "alike": "wood",
        "size": "38x140 C24",
        "size_uuid": "2b3c4d5e-6f70-4182-9394-a5b6c7d8e9f0",
        "erp_code": "W-38140-C24",
        "material": "C24",
        "finish": "planed",
        "parts_count": 240,
        "width": 38,
        "height": 140,
        "total_length_mm": 486200.0,
        "dims": [{ "dim": "38x140x2400", "count": 120 }]
      }
    ],
    "proof": {
      "uuid": "f0e1d2c3-b4a5-4968-8778-695a4b3c2d1e",
      "start_date": "2026-09-14T07:02:00Z",
      "ready_date": null,
      "paused": false,
      "elapsed_seconds": 5720,
      "worked_seconds": 4980,
      "user_name": "tablet_saw",
      "checklist": [],
      "materials": []
    }
  }
}
{
  "part_batch": {
    "uuid": "b1f0c2d3-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
    "name": "PB-2026-014",
    "status": "working",
    "priority": 0,
    "scheduled_prod_date": "2026-09-15",
    "production_line": "Saw line",
    "production_line_uuid": "1c2d3e4f-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
    "project_name": "Vianen",
    "project_uuid": "9d1e7b6a-2f44-4f0a-9a1c-1b2c3d4e5f60",
    "parts_count": 412,
    "ready_count": 0,
    "sizes": [
      {
        "branch": "timber",
        "alike": "wood",
        "size": "38x140 C24",
        "size_uuid": "2b3c4d5e-6f70-4182-9394-a5b6c7d8e9f0",
        "erp_code": "W-38140-C24",
        "material": "C24",
        "finish": "planed",
        "parts_count": 240,
        "width": 38,
        "height": 140,
        "total_length_mm": 486200.0,
        "dims": [{ "dim": "38x140x2400", "count": 120 }]
      }
    ],
    "proof": {
      "uuid": "f0e1d2c3-b4a5-4968-8778-695a4b3c2d1e",
      "start_date": "2026-09-14T07:02:00Z",
      "ready_date": null,
      "paused": false,
      "elapsed_seconds": 5720,
      "worked_seconds": 4980,
      "user_name": "tablet_saw",
      "checklist": [],
      "materials": []
    }
  }
}

alike is the geometry family of the branch, taken from the project's IFC parsing setup: wood (measured by length), plate (measured by area) or generic (not measured). Profiles report width/height/total_length_mm, plates report thickness/total_area_mm2.

employees__client_ids

The people the event belongs to, using your id for each of them — the client_id on their user account. When someone has no client_id, their produuz.it alias is sent instead, so the list always has one entry per person and stays aligned with employee_aliases.

json
{ "employees__client_ids": ["EMP-0042", "PIET"] }
{ "employees__client_ids": ["EMP-0042", "PIET"] }

If you need to know for certain whether a value is your id or a fallback alias, use serialized_employees and read client_id explicitly.

serialized_employees

The same people as full rows. The key is employees__details, not employees, because serialized_event already uses employees for the list of account ids.

json
{
  "employees__details": [
    { "id": 312, "alias": "JAN", "name": "Jan de Vries", "client_id": "EMP-0042" },
    { "id": 318, "alias": "PIET", "name": "Piet Bakker", "client_id": "" }
  ]
}
{
  "employees__details": [
    { "id": 312, "alias": "JAN", "name": "Jan de Vries", "client_id": "EMP-0042" },
    { "id": 318, "alias": "PIET", "name": "Piet Bakker", "client_id": "" }
  ]
}

nicelabel_print

Builds a NiceLabel cloud print job from the panel the event is about and merges it at the top level of the body. Selecting it reveals three extra fields: Printer, FilePath and API version (v1 or v2). Full walkthrough and example payloads in the NiceLabel tutorial.

Events with a fixed payload

report_deprecation, rack_load_ready and rack_load_delivered are not production events, so Payload content does not apply to them. They always send their own block. Custom payload fields, headers and authentication work as usual.

report_deprecation

json
{
  "event": "report_deprecation",
  "design_name": "ML-0B-5",
  "design_uuid": "db0cb2a6-8fbf-46ac-81f5-ece6faca584e",
  "design_level": "assembly",
  "design_revision": 3,
  "project_uuid": "9d1e7b6a-2f44-4f0a-9a1c-1b2c3d4e5f60",
  "project_name": "Vianen",
  "affected_reports": [
    { "uuid": "c3d4e5f6-a7b8-4901-8234-56789abcdef0", "name": "Wood BoM", "document_type": "wood_bom" }
  ]
}
{
  "event": "report_deprecation",
  "design_name": "ML-0B-5",
  "design_uuid": "db0cb2a6-8fbf-46ac-81f5-ece6faca584e",
  "design_level": "assembly",
  "design_revision": 3,
  "project_uuid": "9d1e7b6a-2f44-4f0a-9a1c-1b2c3d4e5f60",
  "project_name": "Vianen",
  "affected_reports": [
    { "uuid": "c3d4e5f6-a7b8-4901-8234-56789abcdef0", "name": "Wood BoM", "document_type": "wood_bom" }
  ]
}

rack_load_ready and rack_load_delivered

The load, the rack setup it uses and every panel on it with its place on the rack, under rack_trace.

json
{
  "event": "rack_load_delivered",
  "rack_trace": {
    "uuid": "5f6a7b8c-9d0e-4f12-8345-6789abcdef01",
    "description": "Truck 2, morning",
    "location": "Yard A",
    "status": "loaded",
    "project_name": "Vianen",
    "project_uuid": "9d1e7b6a-2f44-4f0a-9a1c-1b2c3d4e5f60",
    "project_number": "5073",
    "rack_setup": {
      "uuid": "aabbccdd-1122-4334-8556-778899aabbcc",
      "name": "A-frame 6m",
      "type": "middle",
      "length": 6000,
      "width": 1200,
      "height": 3000
    },
    "panels": [
      {
        "uuid": "e8f7e7f7-3b27-41e0-bb78-99eae64ba7cd",
        "design_name": "ML-0B-5",
        "status": "ready",
        "delivery_status": "delivered",
        "client_order_id": "1030622",
        "batch_name": "Batch 1",
        "rack_side": "a",
        "rack_row": 2,
        "rack_row_pos": 1,
        "strapped_to_frame": false,
        "width": 3000,
        "height": 2800,
        "thickness": 300
      }
    ],
    "panels_count": 1
  }
}
{
  "event": "rack_load_delivered",
  "rack_trace": {
    "uuid": "5f6a7b8c-9d0e-4f12-8345-6789abcdef01",
    "description": "Truck 2, morning",
    "location": "Yard A",
    "status": "loaded",
    "project_name": "Vianen",
    "project_uuid": "9d1e7b6a-2f44-4f0a-9a1c-1b2c3d4e5f60",
    "project_number": "5073",
    "rack_setup": {
      "uuid": "aabbccdd-1122-4334-8556-778899aabbcc",
      "name": "A-frame 6m",
      "type": "middle",
      "length": 6000,
      "width": 1200,
      "height": 3000
    },
    "panels": [
      {
        "uuid": "e8f7e7f7-3b27-41e0-bb78-99eae64ba7cd",
        "design_name": "ML-0B-5",
        "status": "ready",
        "delivery_status": "delivered",
        "client_order_id": "1030622",
        "batch_name": "Batch 1",
        "rack_side": "a",
        "rack_row": 2,
        "rack_row_pos": 1,
        "strapped_to_frame": false,
        "width": 3000,
        "height": 2800,
        "thickness": 300
      }
    ],
    "panels_count": 1
  }
}

rack_side is a or ba is the only side of a single-sided rack. status on the load is a free-text note typed by the person who made it, not a computed state; the state you want is delivery_status on each panel.

A rack load spans whatever production lines its panels came from, so it is not line-specific: it reaches every webhook subscribed to the event.

Delivery log

Every call is recorded: when it was made, which event it was about, the status code your endpoint returned, how long it took and, when it failed, the error.

The Last delivery column in the webhooks table shows when the webhook last fired, coloured by how that call went:

Meaning
GreenThe last call was accepted (200).
RedThe last call was refused, errored, or got no answer at all. The tooltip says which.
GreyNothing has been sent yet. A webhook whose event has not happened is not a problem.

The 🕘 button on the row opens the log itself: the last 20 calls with their status, duration and error. That is where to look when an integration stops working — a column of red rows with ConnectTimeout says your endpoint is unreachable, and a column of 401 says the secret no longer matches.

Some details worth knowing:

  • Records are kept for 30 days and then deleted. The log answers "what happened this week", not "what happened last quarter".
  • The request body is kept only for failed calls, so you can see exactly what was being sent when it failed. A successful call keeps no copy.
  • Headers are never stored. They carry your secret, and a log is read by more people than a settings form.

Custom payload fields

Beyond the payload blocks, you can add your own name/value pairs, merged into the body of every request this webhook sends. Useful for constants your endpoint expects: a plant identifier, a routing key, an environment tag.

Each field has a name (the JSON key) and a value (a text string). If a custom field uses the same name as a standard field, the standard field wins and your value is dropped. Pick names that cannot collide.

Authentication and headers

  • Auth header type + Secret send Authorization: <type> <secret>, for example Authorization: Bearer eyJhbGci.... Choose None to send no Authorization header.
  • Custom headers add any other header your endpoint requires, such as Ocp-Apim-Subscription-Key for NiceLabel.

produuz.it does not sign the body. The secret is what proves the request came from your account, so use HTTPS.

Use cases

  • ERP integration — sync production status to your business system.
  • Label printing — print a label when a panel starts (see the NiceLabel tutorial).
  • Notifications — send alerts to Slack, Teams or another messaging platform.
  • Logistics — tell your transport system a rack load is ready to leave.
  • Quality tracking — log production events in an external quality system.