# Authentication (https://docs.pulsarml.com/authentication) How to authenticate requests against the Pulsar Public API. Every request is authenticated with an API key, sent as the `x-api-key` header: ```bash curl --location 'https://draco.prod.pulsarml.com/v1/machines' \ --header 'Accept: application/json' \ --header 'x-api-key: ' ``` API keys are not self-service. To get one, contact your Customer Success Manager (CSM) and they will send it to you. Production and the development sandbox each use their own key — see [Environments](/environments). Every endpoint's playground has an **Authorization** field for `x-api-key` — paste your key there once and it applies to every endpoint's playground and generated code samples on this site automatically (stored in your browser only). # Environments (https://docs.pulsarml.com/environments) Production and the development sandbox, and when to use each. The Pulsar Public API runs in two environments. They expose the same endpoints; only the base URL and the API key change. | Environment | Base URL | Use it for | | ----------- | --------------------------------- | ------------------------------------- | | Production | `https://draco.prod.pulsarml.com` | Your live integration | | Development | `https://draco.dev.pulsarml.com` | Building and testing your integration | Every example on this site uses the production URL. To call the development environment instead, swap the base URL and use your development API key: ```bash curl --location 'https://draco.dev.pulsarml.com/v1/machines' \ --header 'Accept: application/json' \ --header 'x-api-key: ' ``` ## The development environment [#the-development-environment] Development is a sandbox for testing the API with your real factory data. Use it to build and try out your integration before you point it at production. ## API keys [#api-keys] Each environment has its own API key. A production key does not work against development, and a development key does not work against production. Development API keys are requested the same way as production ones: contact your Customer Success Manager (CSM) and they will send it to you. Every endpoint's playground has a server selector. Pick `https://draco.dev.pulsarml.com` there and paste your development key in the **Authorization** field to send requests to the sandbox. # Overview (https://docs.pulsarml.com/) Everything you need to integrate with the Pulsar Public API. The Pulsar Public API exposes machines, production logs, operators, SKUs, reports, and realtime/nominal-speed data. This site is generated directly from our OpenAPI spec, so it always reflects what's actually deployed. ## Getting started [#getting-started] ## Using an AI assistant [#using-an-ai-assistant] Give your assistant this link and ask it to build the integration: [https://docs.pulsarml.com/llms-full.txt](https://docs.pulsarml.com/llms-full.txt) It is the whole site as one Markdown file, every endpoint included. Tools that work from a spec can use [https://docs.pulsarml.com/openapi.json](https://docs.pulsarml.com/openapi.json) instead. # Events (https://docs.pulsarml.com/webhooks/events) Every webhook event type, when it fires, and the fields it carries. Each notification's `eventType` says what happened, and its `event` object carries the data. The [envelope](/webhooks#body) around it is the same for every event. | Event type | Sent when | | ---------------------------------------------- | ----------------------------------- | | [`MachineStopClassificationCreated`](#created) | A machine stop is classified. | | [`MachineStopClassificationUpdated`](#updated) | A stop's classification changes. | | [`MachineStopClassificationDeleted`](#deleted) | A stop's classification is removed. | | [`Ping`](/webhooks#ping-events) | Pulsar tests your endpoint. | ## Machine stop classifications [#machine-stop-classifications] When a machine stops, Pulsar records the stop. **Classifying** it means recording why it happened: a general cause, optionally a more specific cause and the component involved, and a free-text description. A classification is entered by a person or applied automatically by a rule. All three classification events carry the same `event` object: | Field | Type | Description | | --------------------- | -------------- | ------------------------------------------------------------------------------------------------- | | `machine` | string | The machine's name in Pulsar. | | `machineExternalName` | string \| null | The machine's external name, for example its ID in your ERP. `null` if it has none. | | `start` | string | When the stop started, as an ISO 8601 UTC timestamp. | | `end` | string \| null | When the stop ended, as an ISO 8601 UTC timestamp. Can be `null`. | | `operator` | string \| null | Full name of the operator on the machine when the stop started. `null` if nobody was logged in. | | `generalCause` | string \| null | Name of the general cause. | | `specificCause` | string \| null | Name of the specific cause. | | `component` | string \| null | Name of the component involved. | | `description` | string \| null | Free-text description of the stop. | | `isAutomated` | boolean | `true` if the classification was applied automatically by a rule, `false` if a person entered it. | A stop is identified by its machine and its `start`. Use that pair to match an update or deletion to a stop you already know about. Machines, causes, components and operators are sent by name. If one is renamed in Pulsar, later events carry the new name. ### Created [#created] `MachineStopClassificationCreated` is sent when a stop is classified for the first time. ```json { "eventId": "8d2f6c1e-4b7a-4f0e-9a3c-5e1d7b9f2a64", "eventTimestamp": "2026-09-25T14:03:12.482Z", "eventType": "MachineStopClassificationCreated", "event": { "machine": "Injection molder 4", "machineExternalName": "INJ-004", "start": "2026-09-25T13:41:07.000Z", "end": "2026-09-25T13:58:30.000Z", "operator": "Ana Torres", "generalCause": "Mechanical failure", "specificCause": "Mold jam", "component": "Ejector pin", "description": "Part stuck in cavity 2, cleared manually", "isAutomated": false } } ``` ### Updated [#updated] `MachineStopClassificationUpdated` is sent when an existing classification changes, for example when someone corrects the cause. The `event` object carries the classification's current values. ```json { "eventId": "c41a7e09-2d6b-4f38-9e15-7b0c3a9d4f22", "eventTimestamp": "2026-09-25T14:20:45.117Z", "eventType": "MachineStopClassificationUpdated", "event": { "machine": "Injection molder 4", "machineExternalName": "INJ-004", "start": "2026-09-25T13:41:07.000Z", "end": "2026-09-25T13:58:30.000Z", "operator": "Ana Torres", "generalCause": "Mechanical failure", "specificCause": "Ejector failure", "component": "Ejector pin", "description": "Ejector pin bent, replaced", "isAutomated": false } } ``` ### Deleted [#deleted] `MachineStopClassificationDeleted` is sent when a classification is removed, leaving the stop unclassified. The causes, component, description and `isAutomated` describe the classification **as it was before it was deleted**. The machine, stop times and operator come from Pulsar's current records. ```json { "eventId": "5e93b0d7-8a14-4c6f-b2e8-1d7f0a3c9b56", "eventTimestamp": "2026-09-25T15:02:09.804Z", "eventType": "MachineStopClassificationDeleted", "event": { "machine": "Injection molder 4", "machineExternalName": "INJ-004", "start": "2026-09-25T13:41:07.000Z", "end": "2026-09-25T13:58:30.000Z", "operator": "Ana Torres", "generalCause": "Mechanical failure", "specificCause": "Ejector failure", "component": "Ejector pin", "description": "Ejector pin bent, replaced", "isAutomated": false } } ``` ## Event data is read at delivery time [#event-data-is-read-at-delivery-time] For created and updated events, Pulsar reads the classification when it first prepares the notification, not at the instant of the change. If a classification changes twice in quick succession, both events can carry the newer values. Treat each event as "this stop's classification now looks like this", not as a record of one particular edit. A retry or replay resends exactly the same body as the first attempt, so it never picks up later changes. # Webhooks (https://docs.pulsarml.com/webhooks) Get notified the moment a machine stop is classified, reclassified or unclassified in Pulsar. Instead of polling the API, you can have Pulsar call your application when something happens. A webhook is an HTTPS endpoint you host. Pulsar sends it a `POST` request with a JSON body every time an event you subscribed to occurs. Webhooks currently cover [machine stop classifications](/webhooks/events): Pulsar notifies you when a stop is classified, when a classification changes, and when one is deleted. ## Set up a webhook [#set-up-a-webhook] ### Build an endpoint [#build-an-endpoint] Add a route to your application that accepts a `POST` with a JSON body and answers with a `2XX` status. It has to meet the [endpoint requirements](#endpoint-requirements) below. ### Ask Pulsar to register it [#ask-pulsar-to-register-it] Send your Pulsar contact: * **Name**: a label for the endpoint, such as `ERP`. * **URL**: the full HTTPS URL of your endpoint. * **Event type**: the [event](/webhooks/events) it should receive. Each registration receives one event type. To receive several, register the same URL once per event type, and branch on `eventType` in your handler. ### Store your signing secret [#store-your-signing-secret] Pulsar gives you a **signing secret**. Every request Pulsar sends is signed with it, so you can [verify it really came from Pulsar](/webhooks/signatures). Keep it out of source control, like any other credential. There is one secret per company, shared by all your endpoints. ### Send a test ping [#send-a-test-ping] Ask Pulsar to ping your endpoint. You'll receive a [`Ping` event](#ping-events), which confirms that your endpoint is reachable and your signature check passes, before real events start arriving. ## Endpoint requirements [#endpoint-requirements] Pulsar only delivers to endpoints that are: * **Public HTTPS.** The URL must use `https://`, be at most 255 characters, and contain no credentials (`user:pass@`) or `#fragment`. The hostname must resolve to public IP addresses. Private, loopback and link-local addresses are refused. * **Fast.** Each request has a **10-second** limit, including DNS lookup and connection setup. * **Final.** Redirects are **not followed**. A `3XX` response counts as a failed delivery, so register the final URL. ## The request [#the-request] Every notification is a `POST` with a JSON body and these headers: ```http POST /webhooks/pulsar HTTP/1.1 Host: example.com Content-Type: application/json X-Pulsar-Signature: 5c0e2f4a1b9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f X-Pulsar-Event-Type: MachineStopClassificationCreated X-Pulsar-Event-Id: 8d2f6c1e-4b7a-4f0e-9a3c-5e1d7b9f2a64 X-Pulsar-Timestamp: 2026-09-25T14:03:12.482Z X-Pulsar-Request-Id: 3f9b2c7d-1e4a-4c8b-b6d2-9a0e5f7c1b38 { "eventId": "8d2f6c1e-4b7a-4f0e-9a3c-5e1d7b9f2a64", "eventTimestamp": "2026-09-25T14:03:12.482Z", "eventType": "MachineStopClassificationCreated", "event": { ... } } ``` ### Headers [#headers] | Header | Description | | --------------------- | ----------------------------------------------------------------------------------------------------- | | `Content-Type` | Always `application/json`. | | `X-Pulsar-Signature` | HMAC-SHA256 signature of the raw body. [Verify it](/webhooks/signatures) before trusting the request. | | `X-Pulsar-Event-Type` | The event type, same as `eventType` in the body. | | `X-Pulsar-Event-Id` | The event's ID, same as `eventId` in the body. | | `X-Pulsar-Timestamp` | Same as `eventTimestamp` in the body. | | `X-Pulsar-Request-Id` | The ID of this delivery attempt. Include it when you contact Pulsar support about a delivery. | Header names are case-insensitive, so your framework may show them lowercased. ### Body [#body] Every notification has the same envelope: | Field | Type | Description | | ---------------- | ------ | --------------------------------------------------------------------------------------------------------------- | | `eventId` | string | Unique ID of the event. It stays **the same across retries**, so use it to detect duplicates. | | `eventTimestamp` | string | When Pulsar queued the event, as an ISO 8601 UTC timestamp. This is not the time the change happened in Pulsar. | | `eventType` | string | The [event type](/webhooks/events), for example `MachineStopClassificationCreated`. | | `event` | object | The event's data. Its shape depends on `eventType`; see [Events](/webhooks/events). | ## Respond with a 2XX, quickly [#respond-with-a-2xx-quickly] Return a `2XX` status as soon as you've [verified the signature](/webhooks/signatures). Pulsar reads only the status code and ignores the response body. * Return `200` if you've already handled the event. * Return `202` if you've queued it for later. Do anything slow, like calling other systems or heavy database work, in a background job after responding. A response that takes longer than 10 seconds counts as a failure and is retried, even if your code eventually finishes. ## Retries [#retries] What Pulsar does with a failed delivery depends on why it failed: | Your endpoint... | Pulsar... | | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Returns `2XX` | Marks the delivery as done. | | Times out, can't be reached, or returns `408`, `429` or `5XX` | Makes up to 3 attempts in quick succession, with exponential backoff, honoring a short `Retry-After` header. If all 3 fail, it tries the delivery again later. | | Returns any other `4XX`, or a `3XX` redirect | **Does not retry.** The delivery is recorded as failed, and Pulsar can replay it once your endpoint is fixed. | Pulsar treats `400`, `401`, `403`, `404` and most other `4XX` codes as your endpoint deliberately rejecting the event, so it doesn't retry them. If a bug in your handler returns a `4XX` for valid events, those events wait until Pulsar replays them. Use `5XX` for temporary problems on your side. A failing endpoint doesn't hold up deliveries to your other endpoints. ## Duplicates and ordering [#duplicates-and-ordering] Delivery is **at least once**. Occasionally the same event reaches your endpoint more than once, for example when your endpoint processed a request but Pulsar didn't record the response in time. A retried event also arrives later than events that went through on the first try, so order isn't guaranteed. Make your handler safe to run twice: store each `eventId` you've processed, and skip one you've already seen. A retry or a replay always carries the original `eventId`. ## Ping events [#ping-events] A ping tests your endpoint without waiting for a real event. It uses the same headers and signature as any other notification, has `eventType` set to `Ping`, and its `event` is always `{ "text": "ping" }`: ```json { "eventId": "0b6f3d2a-9c1e-4a7b-8f5d-2e4c6a8b0d1f", "eventTimestamp": "2026-09-25T14:00:00.000Z", "eventType": "Ping", "event": { "text": "ping" } } ``` A ping can reach an endpoint whatever event type it's registered for, so reply `2XX` to `Ping` rather than rejecting event types you didn't expect. ## Next steps [#next-steps] # Verify signatures (https://docs.pulsarml.com/webhooks/signatures) Check that every webhook request really came from Pulsar and wasn't altered on the way. Your endpoint is a public URL, so anyone can send it a request. Every notification from Pulsar carries an `X-Pulsar-Signature` header, a hash of the request body computed with your **signing secret**. Only Pulsar and you know the secret, so a matching signature proves the request came from Pulsar and that the body wasn't changed. The signature is the **HMAC-SHA256** of the **raw request body**, keyed with your signing secret and written as **lowercase hex**. The signing secret is a 32-character string. Use its text (UTF-8 bytes) as the HMAC key, as-is. **Don't Base64-decode or hex-decode it.** Decoding usually succeeds without an error but produces the wrong key, so every signature check fails. ## How to verify a request [#how-to-verify-a-request] ### Read the raw body [#read-the-raw-body] Compute the signature over the **exact bytes** Pulsar sent, before any JSON parsing. Parsing and re-serializing the JSON can change spacing or key order, which changes the hash. Most frameworks give you the raw body separately; see the samples below. ### Compute the expected signature [#compute-the-expected-signature] Compute HMAC-SHA256 of the raw body using your signing secret as the key, and hex-encode the result. ### Compare it with the header [#compare-it-with-the-header] Compare your result with the `X-Pulsar-Signature` header using a **constant-time** comparison (`hmac.compare_digest` in Python, `crypto.timingSafeEqual` in Node.js). A plain `==` can leak, through response timing, how much of a guessed signature was right. ### Accept or reject [#accept-or-reject] If the signatures match, handle the event and return `2XX`. If the header is missing or doesn't match, return `401` and ignore the request. Pulsar doesn't retry a `4XX` response (see [Retries](/webhooks#retries)). So if your secret is misconfigured, real events are rejected and stay undelivered until Pulsar replays them. Ask for a [ping](/webhooks#ping-events) and confirm your endpoint returns `2XX` before you rely on it. ## Sample code [#sample-code] Both samples read the secret from a `PULSAR_WEBHOOK_SECRET` environment variable. ```python import hashlib import hmac import os from flask import Flask, request app = Flask(__name__) SECRET = os.environ["PULSAR_WEBHOOK_SECRET"].encode("utf-8") @app.post("/webhooks/pulsar") def pulsar_webhook(): signature = request.headers.get("X-Pulsar-Signature", "") # The exact bytes Pulsar sent, before any JSON parsing. body = request.get_data() expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, signature): return "", 401 notification = request.get_json() if notification["eventType"] == "Ping": return "", 200 # Queue the work and respond straight away; see "Respond with a 2XX, quickly". enqueue(notification) return "", 202 ``` ```js const crypto = require('node:crypto'); const express = require('express'); const app = express(); const SECRET = process.env.PULSAR_WEBHOOK_SECRET; // express.raw keeps the exact bytes Pulsar sent. Don't use express.json() // on this route: it parses the body before you can check the signature. app.post('/webhooks/pulsar', express.raw({ type: 'application/json' }), (req, res) => { const signature = Buffer.from(req.get('X-Pulsar-Signature') ?? ''); const expected = Buffer.from( crypto.createHmac('sha256', SECRET).update(req.body).digest('hex'), ); if (signature.length !== expected.length || !crypto.timingSafeEqual(signature, expected)) { return res.sendStatus(401); } const notification = JSON.parse(req.body.toString('utf8')); if (notification.eventType === 'Ping') return res.sendStatus(200); // Queue the work and respond straight away; see "Respond with a 2XX, quickly". enqueue(notification); res.sendStatus(202); }); ``` ## Replayed requests [#replayed-requests] The signature covers the body only, so anyone who gets hold of a copy of a request, for example from a log, could send it to you again unchanged and it would still pass the check. Protect against that the same way you [handle duplicates](/webhooks#duplicates-and-ordering): record each `eventId` you've processed and skip one you've already seen. Don't reject requests just because `eventTimestamp` is old. Retries and replays deliberately resend the original event, with its original timestamp, possibly much later. # API Reference (https://docs.pulsarml.com/v1/api-reference) Every endpoint in the Pulsar Public API, grouped by resource. Browse endpoints using the sidebar, grouped by resource (Machines, Operators, SKUs, Production logs, Reports, and so on). Each endpoint page includes parameters, request/response schemas, and a live request builder with generated code samples. # List machines (https://docs.pulsarml.com/v1/api-reference/machines/get-machines) Return every machine the caller's company can see. Machines are the entry point to the rest of the API. Signals take the `uuid` returned here; production logs, nominal speeds, realtime and reports still take the integer `id`. `uuid` is the machine's stable public identifier; it is null for machines that do not have one yet, and those cannot be queried for signals. The list is not paginated and takes no filters — it is scoped to the company resolved from the credential, so there is no company parameter to pass. ## `GET /v1/machines` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Responses #### 200 — Every machine visible to the authenticated company Type: array of object - `id` (integer, required, e.g. `1`) — The unique identifier for the machine. - `uuid` (string (uuid), required, nullable, e.g. `"01748f7c-9688-7abc-8def-0123456789ab"`) — The stable public identifier for the machine. Null for machines that do not have one yet. - `name` (string, required, e.g. `"machine 1"`) — The name of the machine. - `externalName` (string, optional, nullable, e.g. `"EXT-1"`) — The external name of the machine, if any. - `isActive` (boolean, required, e.g. `true`) — Whether the machine is currently active. - `isCountEnabled` (boolean, required, e.g. `true`) — Whether counting is enabled for the machine. ### Example request ```bash curl -X GET 'https://draco.prod.pulsarml.com/v1/machines' \ -H 'x-api-key: ' ``` # Delete a nominal speed (https://docs.pulsarml.com/v1/api-reference/nominal-speeds/delete-nominal-speeds-nominal_speed_id) Delete a machine/SKU speed configuration. With no nominal speed for a pair, there is nothing to compare actual output against for that combination. ## `DELETE /v1/nominal-speeds/{nominal_speed_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `nominal_speed_id` (string (uuid4), required) — The nominal speed ID. ### Responses #### 204 — Successful Response #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X DELETE 'https://draco.prod.pulsarml.com/v1/nominal-speeds/' \ -H 'x-api-key: ' ``` # Retrieve a nominal speed (https://docs.pulsarml.com/v1/api-reference/nominal-speeds/get-nominal-speeds-nominal_speed_id) Return one nominal-speed record by id. ## `GET /v1/nominal-speeds/{nominal_speed_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `nominal_speed_id` (string (uuid4), required) — The nominal speed ID. ### Responses #### 200 — The nominal speed record Type: object (`GetNominalSpeedByIdResponseV1`) - `id` (string, required, e.g. `"36f7b519-d93e-4ec8-8245-7f4b8c123456"`) — The unique identifier for the nominal speed. - `machine` (object, required) — The machine associated with the nominal speed. - `id` (integer, required, e.g. `1`) — The unique identifier for the machine. - `name` (string, required, e.g. `"machine 1"`) — The name of the machine. - `sku` (object, required) — The sku associated with the nominal speed. - `id` (string, required, e.g. `"f1c3a9e9-1cef-4d1d-a6d7-e18607cbfa55"`) — The unique identifier for the sku. - `name` (string, required, e.g. `"sku 1"`) — The name of the sku. - `description` (string, required, e.g. `"sku 1 description"`) — The description of the sku. - `unit` (string, required, e.g. `"sku 1 unit"`) — The unit of the sku. - `nominalSpeed` (number, required, e.g. `100`) — The nominal speed of the machine. - `countingFactor` (number, required, e.g. `1`) — The counting factor of the sensor associated with the machine and sku. - `createdAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the nominal speed was created. - `updatedAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the nominal speed was last updated. - `deletedAt` (string (date-time), optional, nullable, e.g. `"2024-01-02T00:00:00.000Z"`) — The timestamp when the nominal speed was deleted, if applicable. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X GET 'https://draco.prod.pulsarml.com/v1/nominal-speeds/' \ -H 'x-api-key: ' ``` # Update a nominal speed (https://docs.pulsarml.com/v1/api-reference/nominal-speeds/patch-nominal-speeds-nominal_speed_id) Partially update a nominal speed: send only the fields that change. Nominal speed is the denominator of the performance term in OEE, so changing it changes the OEE reported for the periods it applies to. ## `PATCH /v1/nominal-speeds/{nominal_speed_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `nominal_speed_id` (string (uuid4), required) — The nominal speed ID. ### Request body (`application/json`, required) Type: object (`UpdateNominalSpeedByIdRequestV1`) - `nominalSpeed` (number, optional, nullable, e.g. `100`) — The nominal speed of the machine and sku - `countingFactor` (number, optional, nullable, e.g. `1`) — The counting factor of the machine and sku - `machine` (object, optional, nullable) — The machine associated with the sku. - `id` (integer, optional, nullable, > 0, e.g. `1`) — The id of the machine - `name` (string, optional, nullable, e.g. `"Machine 1"`) — The name of the machine - `sku` (object, optional, nullable) — The sku associated with the machine. - `id` (string, optional, nullable, e.g. `"9ce2c6b4-a1f4-4bf2-b5c8-10436d1018cc"`) — The id of the sku - `name` (string, optional, nullable, e.g. `"sku 1"`) — The name of the sku ### Responses #### 204 — Successful Response #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X PATCH 'https://draco.prod.pulsarml.com/v1/nominal-speeds/' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "nominalSpeed": 100, "countingFactor": 1 }' ``` # Search nominal speeds (https://docs.pulsarml.com/v1/api-reference/nominal-speeds/post-nominal-speeds-filters) Search the configured speeds for machine and SKU combinations. Useful for auditing: a machine/SKU pair with no nominal speed, or an obviously wrong one, is the most common cause of a performance figure nobody believes. ## `POST /v1/nominal-speeds/filters` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Request body (`application/json`, required) Type: object (`GetNominalSpeedsByFiltersRequestV1`) - `page` (integer, optional, default `1`, > 0, e.g. `1`) — The page number for pagination - `limit` (integer, optional, default `10`, > 0, e.g. `10`) — The number of items per page for pagination - `machines` (object, optional, nullable) — Filter criteria for machines - `search` (string, optional, nullable) — Search term for Machines - `include` (array of integer, optional, nullable) — Items to include by id - `exclude` (array of integer, optional, nullable) — Items to exclude by id - `skus` (object, optional, nullable) — Filter criteria for skus - `search` (string, optional, nullable) — Search term for Skus - `include` (array of string, optional, nullable) — Items to include by id - `exclude` (array of string, optional, nullable) — Items to exclude by id - `nominalSpeed` (object, optional, nullable) — Filter criteria for nominal speeds - `greaterThan` (number, optional, nullable, e.g. `1`) — Greater than value - `greaterThanOrEqual` (number, optional, nullable, e.g. `1`) — Greater than or equal to value - `lessThan` (number, optional, nullable, e.g. `1`) — Less than value - `lessThanOrEqual` (number, optional, nullable, e.g. `1`) — Less than or equal to value - `equal` (number, optional, nullable, e.g. `1`) — Equal value - `notEqual` (number, optional, nullable, e.g. `1`) — Not equal value ### Responses #### 200 — A page of nominal speeds, with the total count and page count Type: object (`GetNominalSpeedsByFiltersResponseV1`) - `totalCount` (integer, optional, default `100`) — The total count of items - `totalPages` (integer, optional, default `10`) — The total number of pages - `currentPage` (integer, optional, default `1`) — The current page - `nominalSpeeds` (array of object, required) - `id` (string, required, e.g. `"36f7b519-d93e-4ec8-8245-7f4b8c123456"`) — The unique identifier for the nominal speed. - `machine` (object, required) — The machine associated with the nominal speed. - `id` (integer, required, e.g. `1`) — The unique identifier for the machine. - `name` (string, required, e.g. `"machine 1"`) — The name of the machine. - `sku` (object, required) — The sku associated with the nominal speed. - `id` (string, required, e.g. `"f1c3a9e9-1cef-4d1d-a6d7-e18607cbfa55"`) — The unique identifier for the sku. - `name` (string, required, e.g. `"sku 1"`) — The name of the sku. - `description` (string, required, e.g. `"sku 1 description"`) — The description of the sku. - `unit` (string, required, e.g. `"sku 1 unit"`) — The unit of the sku. - `nominalSpeed` (number, required, e.g. `100`) — The nominal speed of the machine. - `countingFactor` (number, required, e.g. `1`) — The counting factor of the sensor associated with the machine and sku. - `createdAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the nominal speed was created. - `updatedAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the nominal speed was last updated. - `deletedAt` (string (date-time), optional, nullable, e.g. `"2024-01-02T00:00:00.000Z"`) — The timestamp when the nominal speed was deleted, if applicable. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/nominal-speeds/filters' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "page": 1, "limit": 10, "machines": { "exclude": [ 1 ], "include": [ 1 ], "search": "machine 1" }, "skus": { "exclude": [ "1145d032-975c-4cdb-a378-910aeb3f5865" ], "include": [ "ffce6941-7dcf-4026-a396-0c4927f5bb49" ], "search": "sku 1" }, "nominalSpeed": { "equal": 1, "greaterThan": 1, "greaterThanOrEqual": 1, "lessThan": 1, "lessThanOrEqual": 1, "notEqual": 1 } }' ``` # Create nominal speeds (https://docs.pulsarml.com/v1/api-reference/nominal-speeds/post-nominal-speeds) Set the expected rate for one or many machine/SKU pairs. The body is a JSON **array**. `nominalSpeed` must be greater than zero, and `machine` and `sku` each accept an `id` or a `name`. `countingFactor` defaults to `1` and converts sensor pulses into the SKU's unit — set it to `12` where one pulse means a case of twelve. ## `POST /v1/nominal-speeds` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Request body (`application/json`, required) Type: array of object - `nominalSpeed` (number, required, > 0, e.g. `60`) — The nominal speed of the machine - `countingFactor` (number, optional, default `1`, > 0, e.g. `60`) — The counting factor of the machine - `machine` (object, required) - `id` (integer, optional, nullable, > 0, e.g. `1`) — The id of the machine - `name` (string, optional, nullable, e.g. `"Machine 1"`) — The name of the machine - `sku` (object, required) - `id` (string, optional, nullable, e.g. `"9ce2c6b4-a1f4-4bf2-b5c8-10436d1018cc"`) — The id of the sku - `name` (string, optional, nullable, e.g. `"sku 1"`) — The name of the sku ### Responses #### 200 — The nominal speeds that were created Type: array of object - `id` (string, required, e.g. `"36f7b519-d93e-4ec8-8245-7f4b8c123456"`) — The unique identifier for the nominal speed. - `machine` (object, required) — The machine associated with the nominal speed. - `id` (integer, required, e.g. `1`) — The unique identifier for the machine. - `name` (string, required, e.g. `"machine 1"`) — The name of the machine. - `sku` (object, required) — The sku associated with the nominal speed. - `id` (string, required, e.g. `"f1c3a9e9-1cef-4d1d-a6d7-e18607cbfa55"`) — The unique identifier for the sku. - `name` (string, required, e.g. `"sku 1"`) — The name of the sku. - `description` (string, required, e.g. `"sku 1 description"`) — The description of the sku. - `unit` (string, required, e.g. `"sku 1 unit"`) — The unit of the sku. - `nominalSpeed` (number, required, e.g. `100`) — The nominal speed of the machine. - `countingFactor` (number, required, e.g. `1`) — The counting factor of the sensor associated with the machine and sku. - `createdAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the nominal speed was created. - `updatedAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the nominal speed was last updated. - `deletedAt` (string (date-time), optional, nullable, e.g. `"2024-01-02T00:00:00.000Z"`) — The timestamp when the nominal speed was deleted, if applicable. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/nominal-speeds' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '[ { "nominalSpeed": 60, "countingFactor": 60, "machine": { "id": 1, "name": "Machine 1" }, "sku": { "id": "9ce2c6b4-a1f4-4bf2-b5c8-10436d1018cc", "name": "sku 1" } } ]' ``` # Delete an operator (https://docs.pulsarml.com/v1/api-reference/operators/delete-operators-operator_id) Delete an operator from the roster. Production logs and shifts that reference the operator are left pointing at a record that no longer resolves, so for someone who has simply left, leaving the record in place preserves the history their shifts are attached to. ## `DELETE /v1/operators/{operator_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `operator_id` (integer, required) — The operator ID. ### Responses #### 204 — Successful Response #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X DELETE 'https://draco.prod.pulsarml.com/v1/operators/' \ -H 'x-api-key: ' ``` # Retrieve an operator (https://docs.pulsarml.com/v1/api-reference/operators/get-operators-operator_id) Return one operator by id. ## `GET /v1/operators/{operator_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `operator_id` (integer, required) — The operator ID. ### Responses #### 200 — The operator Type: object (`GetOperatorByIdResponseV1`) - `id` (integer, required, e.g. `1`) — The unique identifier for the operator. - `company` (object, required) — The company associated with the operator. - `id` (integer, required, e.g. `1`) — The unique identifier for the company. - `name` (string, required, e.g. `"company 1"`) — The name of the company. - `firstName` (string, required, e.g. `"Pérez"`) — The first name of the operator. - `lastName` (string, required, nullable, e.g. `"Sánchez"`) — The last name of the operator. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X GET 'https://draco.prod.pulsarml.com/v1/operators/' \ -H 'x-api-key: ' ``` # Update an operator (https://docs.pulsarml.com/v1/api-reference/operators/patch-operators-operator_id) Partially update an operator: send only the fields that change. ## `PATCH /v1/operators/{operator_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `operator_id` (integer, required) — The operator ID. ### Request body (`application/json`, required) Type: object (`UpdateOperatorByIdRequestV1`) - `firstName` (string, optional, nullable, e.g. `"Pérez"`) — The first name of the operator. - `lastName` (string, optional, nullable, e.g. `"Sánchez"`) — The last name of the operator. ### Responses #### 204 — Successful Response #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X PATCH 'https://draco.prod.pulsarml.com/v1/operators/' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "firstName": "Pérez", "lastName": "Sánchez" }' ``` # Search operators (https://docs.pulsarml.com/v1/api-reference/operators/post-operators-filters) Search the operator roster, or page through it with `{}`. There is no combined name filter: `firstName` and `lastName` are separate fuzzy filters and combine with AND. ## `POST /v1/operators/filters` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Request body (`application/json`, required) Type: object (`GetOperatorsByFiltersRequestV1`) - `page` (integer, optional, default `1`, > 0, e.g. `1`) — The page number for pagination - `limit` (integer, optional, default `10`, > 0, e.g. `10`) — The number of items per page for pagination - `firstName` (object, optional, nullable) — Filter criteria for fist name - `search` (string, optional, nullable) — Search term - `lastName` (object, optional, nullable) — Filter criteria for last name - `search` (string, optional, nullable) — Search term ### Responses #### 200 — A page of operators, with the total count and page count Type: object (`GetOperatorsByFiltersResponseV1`) - `totalCount` (integer, optional, default `100`) — The total count of items - `totalPages` (integer, optional, default `10`) — The total number of pages - `currentPage` (integer, optional, default `1`) — The current page - `operators` (array of object, required) - `id` (integer, required, e.g. `1`) — The unique identifier for the operator. - `company` (object, required) — The company associated with the operator. - `id` (integer, required, e.g. `1`) — The unique identifier for the company. - `name` (string, required, e.g. `"company 1"`) — The name of the company. - `firstName` (string, required, e.g. `"Pérez"`) — The first name of the operator. - `lastName` (string, required, nullable, e.g. `"Sánchez"`) — The last name of the operator. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/operators/filters' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "page": 1, "limit": 10, "firstName": { "search": "Pérez" }, "lastName": { "search": "Sánchez" } }' ``` # Create operators (https://docs.pulsarml.com/v1/api-reference/operators/post-operators) Create one or many operators. The body is a JSON **array**, even for a single record. Both `firstName` and `lastName` are optional, so a partially known operator can be created now and completed later with a `PATCH`. ## `POST /v1/operators` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Request body (`application/json`, required) Type: array of object - `firstName` (string, optional, nullable) — The first name of the operator. - `lastName` (string, optional, nullable) — The last name of the operator. ### Responses #### 200 — The operators that were created Type: array of object - `id` (integer, required, e.g. `1`) — The unique identifier for the operator. - `company` (object, required) — The company associated with the operator. - `id` (integer, required, e.g. `1`) — The unique identifier for the company. - `name` (string, required, e.g. `"company 1"`) — The name of the company. - `firstName` (string, required, e.g. `"Pérez"`) — The first name of the operator. - `lastName` (string, required, nullable, e.g. `"Sánchez"`) — The last name of the operator. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/operators' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '[ {} ]' ``` # Delete a Production Log (https://docs.pulsarml.com/v1/api-reference/production-logs/delete-production-logs-production_log_id) Delete a Production Log, and with it its contribution to every metric derived from it. Prefer a `PATCH` when the record is wrong rather than absent. ## `DELETE /v1/production-logs/{production_log_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `production_log_id` (string (uuid4), required) — The production log ID. ### Responses #### 204 — Successful Response #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X DELETE 'https://draco.prod.pulsarml.com/v1/production-logs/' \ -H 'x-api-key: ' ``` # Retrieve a Production Log (https://docs.pulsarml.com/v1/api-reference/production-logs/get-production-logs-production_log_id) Return one Production Log by id, with its related records expanded inline. An id belonging to another company is reported as `404`, not `403`. ## `GET /v1/production-logs/{production_log_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `production_log_id` (string (uuid4), required) — The production log ID. ### Responses #### 200 — The Production Log, with machine, SKU, operator and work order expanded Type: object (`GetProductionLogByIdResponseV1`) - `id` (string, required, e.g. `"36f7b519-d93e-4ec8-8245-7f4b8c123456"`) — The unique identifier for the production log - `type` ("correct" | "defects" | "total", required, e.g. `"correct"`) — The type of production log - `sensorCount` (number, required, e.g. `500`) — The sensor count recorded for this log - `count` (number, required, e.g. `1000`) — The total count of items produced during this log - `nominalSpeed` (number, required, e.g. `60`) — The nominal speed of the machine during this log - `countingFactor` (number, optional, nullable, e.g. `1`) — The counting factor of the machine during this log - `start` (string (date-time), required, e.g. `"2024-01-01T00:00:00"`) — The start time of the production log - `end` (string (date-time), required, e.g. `"2024-01-01T08:00:00"`) — The end time of the production log - `isReviewed` (boolean, required, e.g. `true`) — Indicates if the production log has been reviewed - `isAutomated` (boolean, required, e.g. `true`) — Indicates if the production log was automated - `machineId` (integer, required, e.g. `12`) — The ID of the machine involved in the production log - `createdAt` (string (date-time), required) — The timestamp when the production log was created - `updatedAt` (string (date-time), required) — The timestamp when the production log was last updated - `deletedAt` (string (date-time), optional, nullable) — The timestamp when the production log was deleted, if applicable - `live` (boolean, required, e.g. `true`) — Indicates if the production log is live - `machine` (object, required) — The machine involved in the production log - `id` (integer, required) — The ID of the machine - `name` (string, required) — The name of the machine - `isCountEnabled` (boolean, required) — Indicates if counting is enabled - `externalName` (string, optional, nullable) — The external name of the machine - `company` (object, required) — The company involved in the production log - `id` (integer, required) — The ID of the company - `name` (string, required) — The name of the company - `timezone` (string, required) — The company's timezone string (e.g., "America/New_York") - `shift` (object, required) — The shift during which the production log was recorded - `id` (integer, required) — The ID of the shift - `name` (string, required) — The name of the shift - `start` (string (date-time), required) — The start time of the shift - `end` (string (date-time), required) — The end time of the shift - `isConsidered` (boolean, required) — Indicates if the shift is considered for metrics - `operator` (object, optional) — The user who reviewed the production log - `id` (integer, required) — The ID of the user/operator - `firstName` (string, optional, nullable) — The operator's first name - `lastName` (string, optional, nullable) — The operator's last name - `email` (string, optional, nullable) — The operator's email - `sku` (object, required) — The SKU produced during the production log - `id` (string, required) — The UUID of the SKU - `name` (string, required) — The name of the SKU - `description` (string, optional, nullable) — The description of the SKU - `unit` (string, required) — The unit of measure for the SKU - `workOrder` (object | object (any keys), required) — The work order associated with the production log - when it is `WorkOrderDetailsDtoV1`: - `id` (string, required) — The UUID of the work order - `name` (string, required) — The name of the work order - `description` (string, required) — The description of the work order - `status` ("InProgress" | "Completed" | "Canceled", required) — The status of the work order - `simpleWorkOrder` (object | object (any keys), required) — The simple work order associated with the production log - when it is `SimpleWorkOrderDetailsDtoV1`: - `id` (string, required) — The UUID of the simple work order - `name` (string, required) — The name of the simple work order #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X GET 'https://draco.prod.pulsarml.com/v1/production-logs/' \ -H 'x-api-key: ' ``` # Update a Production Log (https://docs.pulsarml.com/v1/api-reference/production-logs/patch-production-logs-production_log_id) Partially update a Production Log: send only the fields that change. This is the call for correcting a miscount or a wrong time window without restating the whole record. Changing `count`, `nominalSpeed`, `start` or `end` changes the OEE Pulsar reports for that period, and the correction is visible in dashboards immediately. ## `PATCH /v1/production-logs/{production_log_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `production_log_id` (string (uuid4), required) — The production log ID. ### Request body (`application/json`, required) Type: object (`UpdateProductionLogByIdRequestV1`) - `type` ("correct" | "defects" | "total", optional, nullable, e.g. `"correct"`) — The type of the production log (correct, defects, etc.) - `count` (integer, optional, nullable, e.g. `1000`) — The total count of items produced during this production log - `nominalSpeed` (number, optional, nullable, e.g. `60`) — The nominal speed of the machine during this production log (using float for speed) - `start` (string (date-time), optional, nullable, e.g. `"2024-01-01T08:00:00"`) — The start time of the production log - `end` (string (date-time), optional, nullable, e.g. `"2024-01-01T16:00:00"`) — The end time of the production log - `machine` (object, optional, nullable) — The machine associated with the production log - `id` (integer, optional, nullable, > 0, e.g. `1`) — The id of the machine - `name` (string, optional, nullable, e.g. `"Machine 1"`) — The name of the machine - `sku` (object, optional, nullable) — The SKU associated with the production log - `id` (string, optional, nullable, e.g. `"9ce2c6b4-a1f4-4bf2-b5c8-10436d1018cc"`) — The id of the sku - `name` (string, optional, nullable, e.g. `"sku 1"`) — The name of the sku - `operator` (object, optional, nullable) — The operator involved in this production log - `firstName` (string, required, e.g. `"Pérez"`) — The operator's first name - `lastName` (string, required, e.g. `"Sánchez"`) — The operator's last name - `workOrder` (string, optional, nullable, e.g. `"work order 1"`) — The name of the work order associated with this production log ### Responses #### 204 — Successful Response #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X PATCH 'https://draco.prod.pulsarml.com/v1/production-logs/' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "type": "correct", "count": 1000, "nominalSpeed": 60, "start": "2024-01-01T08:00:00", "end": "2024-01-01T16:00:00", "workOrder": "work order 1" }' ``` # Search Production Logs (https://docs.pulsarml.com/v1/api-reference/production-logs/post-production-logs-filters) Search Production Logs by machine, SKU, date, quantity or review state. A `POST` because the filter body nests, but it only reads: the `production_logs.read` scope is sufficient. Send `{}` to get the first page with the defaults (`page` 1, `limit` 10). Filters are optional and combine with AND. ## `POST /v1/production-logs/filters` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Request body (`application/json`, required) Type: object (`GetProductionLogsByFiltersRequestV1`) - `page` (integer, optional, default `1`, > 0, e.g. `1`) — The page number for pagination - `limit` (integer, optional, default `10`, > 0, e.g. `10`) — The number of items per page for pagination - `machines` (object, optional, nullable) — Filter criteria for machines - `search` (string, optional, nullable) — Search term for Machines - `include` (array of integer, optional, nullable) — Items to include by id - `exclude` (array of integer, optional, nullable) — Items to exclude by id - `skus` (object, optional, nullable) — Filter criteria for SKUs - `search` (string, optional, nullable) — Search term for Skus - `include` (array of string, optional, nullable) — Items to include by id - `exclude` (array of string, optional, nullable) — Items to exclude by id - `nominalSpeed` (object, optional, nullable) — Filter criteria for nominal speed - `greaterThan` (number, optional, nullable, e.g. `1`) — Greater than value - `greaterThanOrEqual` (number, optional, nullable, e.g. `1`) — Greater than or equal to value - `lessThan` (number, optional, nullable, e.g. `1`) — Less than value - `lessThanOrEqual` (number, optional, nullable, e.g. `1`) — Less than or equal to value - `equal` (number, optional, nullable, e.g. `1`) — Equal value - `notEqual` (number, optional, nullable, e.g. `1`) — Not equal value - `quantity` (object, optional, nullable) — Filter criteria for quantity - `greaterThan` (number, optional, nullable, e.g. `1`) — Greater than value - `greaterThanOrEqual` (number, optional, nullable, e.g. `1`) — Greater than or equal to value - `lessThan` (number, optional, nullable, e.g. `1`) — Less than value - `lessThanOrEqual` (number, optional, nullable, e.g. `1`) — Less than or equal to value - `equal` (number, optional, nullable, e.g. `1`) — Equal value - `notEqual` (number, optional, nullable, e.g. `1`) — Not equal value - `type` (object, optional, nullable) — Filter criteria for type of production log - `include` (array of "correct" | "defects" | "total", optional, nullable) — Items to include by id - `exclude` (array of "correct" | "defects" | "total", optional, nullable) — Items to exclude by id - `reviewed` (boolean, optional, nullable) — Filter criteria for reviewed status - `startDate` (object, optional, nullable) — Filter criteria for start date - `after` (string (date-time), optional, nullable) — Filter by after date - `before` (string (date-time), optional, nullable) — Filter by before date - `between` (object, optional, nullable) — Filter by date range - `start` (string (date-time), optional, nullable, e.g. `"2024-01-01T00:00:00.000Z"`) — Start date of the range - `end` (string (date-time), optional, nullable, e.g. `"2024-01-31T23:59:59.000Z"`) — End date of the range - `equal` (string (date-time), optional, nullable) — Filter by equal date - `endDate` (object, optional, nullable) — Filter criteria for end date - `after` (string (date-time), optional, nullable) — Filter by after date - `before` (string (date-time), optional, nullable) — Filter by before date - `between` (object, optional, nullable) — Filter by date range - `start` (string (date-time), optional, nullable, e.g. `"2024-01-01T00:00:00.000Z"`) — Start date of the range - `end` (string (date-time), optional, nullable, e.g. `"2024-01-31T23:59:59.000Z"`) — End date of the range - `equal` (string (date-time), optional, nullable) — Filter by equal date ### Responses #### 200 — A page of Production Logs, with the total count and page count Type: object (`GetProductionLogsByFiltersResponseV1`) - `totalCount` (integer, optional, default `100`) — The total count of items - `totalPages` (integer, optional, default `10`) — The total number of pages - `currentPage` (integer, optional, default `1`) — The current page - `productionLogs` (array of object, required) — List of production logs - `id` (string, required, e.g. `"36f7b519-d93e-4ec8-8245-7f4b8c123456"`) — The unique identifier for the production log - `type` ("correct" | "defects" | "total", required, e.g. `"correct"`) — The type of production log - `sensorCount` (number, required, e.g. `500`) — The sensor count recorded for this log - `count` (number, required, e.g. `1000`) — The total count of items produced during this log - `nominalSpeed` (number, required, e.g. `60`) — The nominal speed of the machine during this log - `countingFactor` (number, optional, nullable, e.g. `1`) — The counting factor of the machine during this log - `start` (string (date-time), required, e.g. `"2024-01-01T00:00:00"`) — The start time of the production log - `end` (string (date-time), required, e.g. `"2024-01-01T08:00:00"`) — The end time of the production log - `isReviewed` (boolean, required, e.g. `true`) — Indicates if the production log has been reviewed - `isAutomated` (boolean, required, e.g. `true`) — Indicates if the production log was automated - `machineId` (integer, required, e.g. `12`) — The ID of the machine involved in the production log - `createdAt` (string (date-time), required) — The timestamp when the production log was created - `updatedAt` (string (date-time), required) — The timestamp when the production log was last updated - `deletedAt` (string (date-time), optional, nullable) — The timestamp when the production log was deleted, if applicable - `live` (boolean, required, e.g. `true`) — Indicates if the production log is live - `machine` (object, required) — The machine involved in the production log - `id` (integer, required) — The ID of the machine - `name` (string, required) — The name of the machine - `isCountEnabled` (boolean, required) — Indicates if counting is enabled - `externalName` (string, optional, nullable) — The external name of the machine - `company` (object, required) — The company involved in the production log - `id` (integer, required) — The ID of the company - `name` (string, required) — The name of the company - `timezone` (string, required) — The company's timezone string (e.g., "America/New_York") - `shift` (object, required) — The shift during which the production log was recorded - `id` (integer, required) — The ID of the shift - `name` (string, required) — The name of the shift - `start` (string (date-time), required) — The start time of the shift - `end` (string (date-time), required) — The end time of the shift - `isConsidered` (boolean, required) — Indicates if the shift is considered for metrics - `operator` (object, optional) — The user who reviewed the production log - `id` (integer, required) — The ID of the user/operator - `firstName` (string, optional, nullable) — The operator's first name - `lastName` (string, optional, nullable) — The operator's last name - `email` (string, optional, nullable) — The operator's email - `sku` (object, required) — The SKU produced during the production log - `id` (string, required) — The UUID of the SKU - `name` (string, required) — The name of the SKU - `description` (string, optional, nullable) — The description of the SKU - `unit` (string, required) — The unit of measure for the SKU - `workOrder` (object | object (any keys), required) — The work order associated with the production log - when it is `WorkOrderDetailsDtoV1`: - `id` (string, required) — The UUID of the work order - `name` (string, required) — The name of the work order - `description` (string, required) — The description of the work order - `status` ("InProgress" | "Completed" | "Canceled", required) — The status of the work order - `simpleWorkOrder` (object | object (any keys), required) — The simple work order associated with the production log - when it is `SimpleWorkOrderDetailsDtoV1`: - `id` (string, required) — The UUID of the simple work order - `name` (string, required) — The name of the simple work order #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/production-logs/filters' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "page": 1, "limit": 10, "machines": { "exclude": [ 1 ], "include": [ 1 ], "search": "machine 1" }, "skus": { "exclude": [ "1145d032-975c-4cdb-a378-910aeb3f5865" ], "include": [ "ffce6941-7dcf-4026-a396-0c4927f5bb49" ], "search": "sku 1" } }' ``` # Create Production Logs (https://docs.pulsarml.com/v1/api-reference/production-logs/post-production-logs) Create one or many Production Logs. The body is a JSON **array**, even for a single record. This is the endpoint an MES, ERP or shop-floor terminal uses to push production into Pulsar. `machine` and `sku` are references that accept either an `id` or a `name`, so a system that only knows line names and product codes does not have to resolve Pulsar ids first. A Production Log describes a closed interval: `start` and `end` are both required. To record what is happening in the shift a machine is running right now, use the realtime endpoints instead. ## `POST /v1/production-logs` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Request body (`application/json`, required) Type: array of object - `type` ("correct" | "defects" | "total", optional, default `"total"`, e.g. `"correct"`) — The type of the production log (correct, defects, etc.) - `count` (integer, required, e.g. `1000`) — The total count of items produced during this production log - `nominalSpeed` (number, required, e.g. `60`) — The nominal speed of the machine during this production log (using float for speed) - `start` (string (date-time), required, e.g. `"2024-01-01T08:00:00"`) — The start time of the production log - `end` (string (date-time), required, e.g. `"2024-01-01T16:00:00"`) — The end time of the production log - `machine` (object, required) — The machine associated with the production log - `id` (integer, optional, nullable, > 0, e.g. `1`) — The id of the machine - `name` (string, optional, nullable, e.g. `"Machine 1"`) — The name of the machine - `sku` (object, required) — The SKU associated with the production log - `id` (string, optional, nullable, e.g. `"9ce2c6b4-a1f4-4bf2-b5c8-10436d1018cc"`) — The id of the sku - `name` (string, optional, nullable, e.g. `"sku 1"`) — The name of the sku - `operator` (object, optional, nullable) — The operator involved in this production log - `firstName` (string, required, e.g. `"Pérez"`) — The operator's first name - `lastName` (string, required, e.g. `"Sánchez"`) — The operator's last name - `workOrder` (string, optional, nullable, e.g. `"work order 1"`) — The name of the work order associated with this production log ### Responses #### 200 — The Production Logs that were created Type: array of object - `id` (string, required, e.g. `"36f7b519-d93e-4ec8-8245-7f4b8c123456"`) — The unique identifier for the production log - `type` ("correct" | "defects" | "total", required, e.g. `"correct"`) — The type of production log - `sensorCount` (number, required, e.g. `500`) — The sensor count recorded for this log - `count` (number, required, e.g. `1000`) — The total count of items produced during this log - `nominalSpeed` (number, required, e.g. `60`) — The nominal speed of the machine during this log - `countingFactor` (number, optional, nullable, e.g. `1`) — The counting factor of the machine during this log - `start` (string (date-time), required, e.g. `"2024-01-01T00:00:00"`) — The start time of the production log - `end` (string (date-time), required, e.g. `"2024-01-01T08:00:00"`) — The end time of the production log - `isReviewed` (boolean, required, e.g. `true`) — Indicates if the production log has been reviewed - `isAutomated` (boolean, required, e.g. `true`) — Indicates if the production log was automated - `machineId` (integer, required, e.g. `12`) — The ID of the machine involved in the production log - `createdAt` (string (date-time), required) — The timestamp when the production log was created - `updatedAt` (string (date-time), required) — The timestamp when the production log was last updated - `deletedAt` (string (date-time), optional, nullable) — The timestamp when the production log was deleted, if applicable - `live` (boolean, required, e.g. `true`) — Indicates if the production log is live - `machine` (object, required) — The machine involved in the production log - `id` (integer, required) — The ID of the machine - `name` (string, required) — The name of the machine - `isCountEnabled` (boolean, required) — Indicates if counting is enabled - `externalName` (string, optional, nullable) — The external name of the machine - `company` (object, required) — The company involved in the production log - `id` (integer, required) — The ID of the company - `name` (string, required) — The name of the company - `timezone` (string, required) — The company's timezone string (e.g., "America/New_York") - `shift` (object, required) — The shift during which the production log was recorded - `id` (integer, required) — The ID of the shift - `name` (string, required) — The name of the shift - `start` (string (date-time), required) — The start time of the shift - `end` (string (date-time), required) — The end time of the shift - `isConsidered` (boolean, required) — Indicates if the shift is considered for metrics - `operator` (object, optional) — The user who reviewed the production log - `id` (integer, required) — The ID of the user/operator - `firstName` (string, optional, nullable) — The operator's first name - `lastName` (string, optional, nullable) — The operator's last name - `email` (string, optional, nullable) — The operator's email - `sku` (object, required) — The SKU produced during the production log - `id` (string, required) — The UUID of the SKU - `name` (string, required) — The name of the SKU - `description` (string, optional, nullable) — The description of the SKU - `unit` (string, required) — The unit of measure for the SKU - `workOrder` (object | object (any keys), required) — The work order associated with the production log - when it is `WorkOrderDetailsDtoV1`: - `id` (string, required) — The UUID of the work order - `name` (string, required) — The name of the work order - `description` (string, required) — The description of the work order - `status` ("InProgress" | "Completed" | "Canceled", required) — The status of the work order - `simpleWorkOrder` (object | object (any keys), required) — The simple work order associated with the production log - when it is `SimpleWorkOrderDetailsDtoV1`: - `id` (string, required) — The UUID of the simple work order - `name` (string, required) — The name of the simple work order #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/production-logs' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '[ { "type": "correct", "count": 1000, "nominalSpeed": 60, "start": "2024-01-01T08:00:00", "end": "2024-01-01T16:00:00", "machine": { "id": 1, "name": "Machine 1" }, "sku": { "id": "9ce2c6b4-a1f4-4bf2-b5c8-10436d1018cc", "name": "sku 1" }, "workOrder": "work order 1" } ]' ``` # Add an operator to the current shift (https://docs.pulsarml.com/v1/api-reference/realtime/post-realtime-machine_id-operator) Assign an operator to the shift the machine is running at the moment of the call. Both fields are required: `operatorId` and `start`, the moment the assignment begins. Use it for clock-in at a terminal. This writes to the live shift and takes effect immediately. ## `POST /v1/realtime/{machine_id}/operator` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `machine_id` (integer, required) — The machine ID. ### Request body (`application/json`, required) Type: object (`AddOperatorToCurrentShiftRequestV1`) - `operatorId` (integer, required, e.g. `1`) — The id of the operator to add to the machine's current shift. - `start` (string (date-time), required, e.g. `"2024-01-01T08:00:00"`) — The start time of the operator assignment within the current shift. ### Responses #### 204 — Successful Response #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/realtime//operator' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "operatorId": 1, "start": "2024-01-01T08:00:00" }' ``` # Add a SKU to the current shift (https://docs.pulsarml.com/v1/api-reference/realtime/post-realtime-machine_id-sku) Attach a SKU to the shift the machine is running at the moment of the call. Built for shop-floor terminals and scanners: no shift lookup and no production-log arithmetic. The SKU is given as `skuId` or as `sku` (its name), and the work order as `simpleWorkOrderId` or `simpleWorkOrderName`. This writes to the live shift and is visible in the Pulsar dashboard immediately. ## `POST /v1/realtime/{machine_id}/sku` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `machine_id` (integer, required) — The machine ID. ### Request body (`application/json`, required) Type: object (`AddSkuToCurrentShiftRequestV1`) - `simpleWorkOrderId` (string, optional, nullable, e.g. `"123"`) — The id of the simple work order to add the SKU to. - `simpleWorkOrderName` (string, optional, nullable, e.g. `"WO-001"`) — The name of the simple work order to add the SKU to. - `skuId` (string, optional, nullable, e.g. `"456"`) — The id of the SKU to add to the machine's current shift. - `sku` (string, optional, nullable, e.g. `"sku 1"`) — The name of the SKU to add to the machine's current shift. - `quantity` (number, optional, nullable, e.g. `1000`) — The quantity produced for the SKU. - `start` (string (date-time), optional, nullable, e.g. `"2024-01-01T08:00:00"`) — The start time of the SKU production within the current shift. - `end` (string (date-time), optional, nullable, e.g. `"2024-01-01T16:00:00"`) — The end time of the SKU production within the current shift. - `custom` (object (any keys), optional, nullable) — Custom field values keyed by field id. ### Responses #### 204 — Successful Response #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/realtime//sku' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "simpleWorkOrderId": "123", "simpleWorkOrderName": "WO-001", "skuId": "456", "sku": "sku 1", "quantity": 1000, "start": "2024-01-01T08:00:00", "end": "2024-01-01T16:00:00", "custom": { "fieldId": "value" } }' ``` # Metrics report (https://docs.pulsarml.com/v1/api-reference/reports/post-reports-metrics) Return OEE, availability, quality and lost minutes per machine and shift. Each row carries the shift indicators (available time, downtime split by cause) together with the derived production metrics: `availability`, `effectiveness`, `quality`, `oee` and `netOee`, plus minutes lost to low speed and to low quality. A `POST` that only reads — the `reports.read` scope is sufficient. Prefer this over deriving OEE from production logs: shift boundaries, downtime classification and counting factors are already applied. ## `POST /v1/reports/metrics` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Request body (`application/json`, required) Type: object (`GetMetricsReportRequestV1`) - `page` (integer, optional, default `1`, > 0, e.g. `1`) — The page number for pagination - `limit` (integer, optional, default `10`, > 0, e.g. `10`) — The number of items per page for pagination - `machines` (object, optional, nullable) — Filter criteria for machines - `search` (string, optional, nullable) — Search term for Machines - `include` (array of integer, optional, nullable) — Items to include by id - `exclude` (array of integer, optional, nullable) — Items to exclude by id - `startDate` (object, optional, nullable) — Filter criteria for start date - `after` (string (date-time), optional, nullable) — Filter by after date - `before` (string (date-time), optional, nullable) — Filter by before date - `between` (object, optional, nullable) — Filter by date range - `start` (string (date-time), optional, nullable, e.g. `"2024-01-01T00:00:00.000Z"`) — Start date of the range - `end` (string (date-time), optional, nullable, e.g. `"2024-01-31T23:59:59.000Z"`) — End date of the range - `equal` (string (date-time), optional, nullable) — Filter by equal date - `endDate` (object, optional, nullable) — Filter criteria for end date - `after` (string (date-time), optional, nullable) — Filter by after date - `before` (string (date-time), optional, nullable) — Filter by before date - `between` (object, optional, nullable) — Filter by date range - `start` (string (date-time), optional, nullable, e.g. `"2024-01-01T00:00:00.000Z"`) — Start date of the range - `end` (string (date-time), optional, nullable, e.g. `"2024-01-31T23:59:59.000Z"`) — End date of the range - `equal` (string (date-time), optional, nullable) — Filter by equal date ### Responses #### 200 — A page of per-machine, per-shift metrics Type: object (`GetMetricsReportResponseV1`) - `totalCount` (integer, optional, default `100`) — The total count of items - `totalPages` (integer, optional, default `10`) — The total number of pages - `currentPage` (integer, optional, default `1`) — The current page - `metrics` (array of object, required) - `company` (object, required) — The company associated with the metric report. - `id` (integer, required, e.g. `1`) — The unique identifier for the company. - `name` (string, required, e.g. `"company 1"`) — The name of the company. - `machine` (object, required) — The machine of the metric report. - `id` (integer, required, e.g. `1`) — The unique identifier for the machine. - `name` (string, required, e.g. `"machine 1"`) — The name of the machine. - `shift` (object, required) — The shift of the metric report. - `id` (integer, required, e.g. `1`) — The unique identifier of the shift. - `name` (string, required, e.g. `"shift 1"`) — The name of the shift. - `startTime` (string (date-time), required, e.g. `"2024-01-01T08:00:00.000Z"`) — The start time of the shift. - `endTime` (string (date-time), required, e.g. `"2024-01-01T16:00:00.000Z"`) — The end time of the shift. - `isActive` (boolean, required, e.g. `true`) — Whether the shift is currently active. - `shiftIndicators` (object, required) — Information about the shift indicator. - `indicatorId` (integer, optional, nullable, e.g. `1`) — The unique identifier for the indicator. - `annotatedDowntime` (number, optional, nullable, e.g. `0`) — Annotated downtime in minutes. - `availability` (number, optional, nullable, e.g. `100`) — Availability percentage. - `availableTime` (number, optional, nullable, e.g. `1`) — Available time in minutes. - `earlyStopTime` (number, optional, nullable, e.g. `0`) — Early stop time in minutes. - `lateStartTime` (number, optional, nullable, e.g. `0`) — Late start time in minutes. - `netAvailability` (number, optional, nullable, e.g. `100`) — Net availability percentage. - `noDataTime` (number, optional, nullable, e.g. `0`) — Time with no data in minutes. - `nonProgrammedDowntime` (number, optional, nullable, e.g. `0`) — Non-programmed downtime in minutes. - `programmedDowntime` (number, optional, nullable, e.g. `0`) — Programmed downtime in minutes. - `otherStopsTime` (number, optional, nullable, e.g. `0`) — Other stops time in minutes. - `unavailableTime` (number, optional, nullable, e.g. `0`) — Unavailable time in minutes. - `productionMetrics` (object, required) — Information about the production metrics. - `availability` (number, optional, nullable, e.g. `100`) — Availability percentage. - `netAvailability` (number, optional, nullable, e.g. `100`) — Net availability percentage. - `oee` (number, optional, nullable, e.g. `100`) — Overall Equipment Effectiveness (OEE). - `netOee` (number, optional, nullable, e.g. `100`) — Net OEE. - `quality` (number, optional, nullable, e.g. `100`) — Quality percentage. - `effectiveness` (number, optional, nullable, e.g. `100`) — Effectiveness percentage. - `minutesLostByLowQuality` (number, optional, nullable, e.g. `0`) — Minutes lost due to low quality. - `minutesLostByLowSpeed` (number, optional, nullable, e.g. `0`) — Minutes lost due to low speed. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/reports/metrics' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "page": 1, "limit": 10, "machines": { "exclude": [ 1 ], "include": [ 1 ], "search": "machine 1" } }' ``` # Stops report (https://docs.pulsarml.com/v1/api-reference/reports/post-reports-stops) Return individual machine stops with their duration, cause and classification. This is the drill-down behind the downtime totals in the metrics report. `stopId` accepts the full comparison grammar, so a specific stop or a range of them can be pulled directly. ## `POST /v1/reports/stops` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Request body (`application/json`, required) Type: object (`GetStopsReportRequestV1`) - `page` (integer, optional, default `1`, > 0, e.g. `1`) — The page number for pagination - `limit` (integer, optional, default `10`, > 0, e.g. `10`) — The number of items per page for pagination - `machines` (object, optional, nullable) — Filter criteria for machines - `search` (string, optional, nullable) — Search term for Machines - `include` (array of integer, optional, nullable) — Items to include by id - `exclude` (array of integer, optional, nullable) — Items to exclude by id - `stopId` (object, optional, nullable) — Filter criteria for machines - `include` (array of integer, optional, nullable) — Items to include by id - `exclude` (array of integer, optional, nullable) — Items to exclude by id - `greaterThan` (number, optional, nullable, e.g. `1`) — Greater than value - `greaterThanOrEqual` (number, optional, nullable, e.g. `1`) — Greater than or equal to value - `lessThan` (number, optional, nullable, e.g. `1`) — Less than value - `lessThanOrEqual` (number, optional, nullable, e.g. `1`) — Less than or equal to value - `equal` (number, optional, nullable, e.g. `1`) — Equal value - `notEqual` (number, optional, nullable, e.g. `1`) — Not equal value - `startDate` (object, optional, nullable) — Filter criteria for start date - `after` (string (date-time), optional, nullable) — Filter by after date - `before` (string (date-time), optional, nullable) — Filter by before date - `between` (object, optional, nullable) — Filter by date range - `start` (string (date-time), optional, nullable, e.g. `"2024-01-01T00:00:00.000Z"`) — Start date of the range - `end` (string (date-time), optional, nullable, e.g. `"2024-01-31T23:59:59.000Z"`) — End date of the range - `equal` (string (date-time), optional, nullable) — Filter by equal date - `endDate` (object, optional, nullable) — Filter criteria for end date - `after` (string (date-time), optional, nullable) — Filter by after date - `before` (string (date-time), optional, nullable) — Filter by before date - `between` (object, optional, nullable) — Filter by date range - `start` (string (date-time), optional, nullable, e.g. `"2024-01-01T00:00:00.000Z"`) — Start date of the range - `end` (string (date-time), optional, nullable, e.g. `"2024-01-31T23:59:59.000Z"`) — End date of the range - `equal` (string (date-time), optional, nullable) — Filter by equal date ### Responses #### 200 — A page of stops with their cause and classification Type: object (`GetStopsReportResponseV1`) - `totalCount` (integer, optional, default `100`) — The total count of items - `totalPages` (integer, optional, default `10`) — The total number of pages - `currentPage` (integer, optional, default `1`) — The current page - `stops` (array of object, required) - `classification` (object, required) — The classification of the stop. - `id` (integer, required, nullable, e.g. `"9ce2c6b4-a1f4-4bf2-b5c8-10436d1018cc"`) — The unique identifier of the classification. - `createdAt` (string (date-time), required, nullable, e.g. `"2025-11-01T00:00:00"`) — Timestamp of classification creation. - `updatedAt` (string (date-time), required, nullable, e.g. `"2025-11-01T00:00:00"`) — Timestamp of last classification update. - `component` (object, required) — The component associated with the stop. - `id` (string (uuid), optional, nullable, e.g. `"51d34e1b-a294-4246-8c86-4f9acfe94367"`) — The unique identifier. - `name` (string, optional, nullable, e.g. `"a simple cause"`) — The name of the cause/component. - `specificCause` (object, required) — The specific cause of the stop. - `id` (string (uuid), optional, nullable, e.g. `"51d34e1b-a294-4246-8c86-4f9acfe94367"`) — The unique identifier. - `name` (string, optional, nullable, e.g. `"a simple cause"`) — The name of the cause/component. - `generalCause` (object, required) — The general cause of the stop. - `id` (string (uuid), optional, nullable, e.g. `"51d34e1b-a294-4246-8c86-4f9acfe94367"`) — The unique identifier. - `name` (string, optional, nullable, e.g. `"a simple cause"`) — The name of the cause/component. - `description` (string, optional, nullable, e.g. `"a simple description"`) — A description of the classification. - `company` (object, required) — The company associated with the stop. - `id` (integer, required, e.g. `1`) — The unique identifier for the company. - `name` (string, required, e.g. `"company 1"`) — The name of the company. - `machine` (object, required) — The machine of the stop. - `id` (integer, required, e.g. `1`) — The unique identifier for the machine. - `name` (string, required, e.g. `"machine 1"`) — The name of the machine. - `shift` (object, required) — The shift of the stop. - `id` (integer, required, e.g. `1`) — The unique identifier of the shift. - `name` (string, required, e.g. `"shift 1"`) — The name of the shift. - `startTime` (string (date-time), required, e.g. `"2024-01-01T08:00:00.000Z"`) — The start time of the shift. - `endTime` (string (date-time), required, e.g. `"2024-01-01T16:00:00.000Z"`) — The end time of the shift. - `isActive` (boolean, required, e.g. `true`) — Whether the shift is currently active. - `stop` (object, required) — Information about the stop event. - `id` (integer, required, e.g. `1`) — The unique identifier of the stop event. - `startTime` (string (date-time), required, e.g. `"2025-11-01T00:00:00"`) — The start time of the stop. - `endTime` (string (date-time), required, nullable, e.g. `"2025-11-01T00:00:00"`) — The end time of the stop. - `isProgrammed` (boolean, required, e.g. `true`) — Indicates if the stop was programmed. - `isIncludedInAvailability` (boolean, required, e.g. `true`) — Indicates if the stop affects availability calculation. - `isIncludedInStops` (boolean, required, e.g. `true`) — Indicates if the stop is counted as a production stop. - `isManual` (boolean, required, e.g. `true`) — Indicates if the stop was manually logged. - `createdAt` (string (date-time), required, nullable, e.g. `"2025-11-01T00:00:00"`) — Timestamp of stop record creation. - `updatedAt` (string (date-time), required, nullable, e.g. `"2025-11-01T00:00:00"`) — Timestamp of last stop record update. - `deletedAt` (string (date-time), optional, nullable, e.g. `"2025-11-01T00:00:00"`) — Timestamp of stop record deletion, if applicable. - `durationInMinutes` (number, required, e.g. `30`) — The duration of the stop in minutes. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/reports/stops' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "page": 1, "limit": 10, "machines": { "exclude": [ 1 ], "include": [ 1 ], "search": "machine 1" }, "stopId": { "equal": 1, "exclude": [ 1 ], "greaterThan": 1, "greaterThanOrEqual": 1, "include": [ 1 ], "lessThan": 1, "lessThanOrEqual": 1, "notEqual": 1 } }' ``` # Read signal data (https://docs.pulsarml.com/v1/api-reference/signals/get-machines-machine_uuid-signals-public_id) Return aggregated raw sensor points for one signal. Samples are bucketed before they are returned. The bucket width is `agg_period` x `agg_unit` (`agg_period=15`, `agg_unit=m` is a 15-minute bucket) and `agg_function` is how samples inside a bucket are reduced. By default points are returned as a `data` list of `{timestamp, value}` objects. Pass `transpose=true` to get two arrays of equal length instead, `timestamps` and `values`, where `values[i]` belongs to `timestamps[i]`. It carries the same points in a smaller body. Two limits are enforced and both reject the request rather than truncating the response: the window between `start_time` and `end_time` may not exceed **14 days**, and a query may not produce more than **10,000 points**. The defaults (`agg_unit=s`, `agg_period=1`) reach the point ceiling after under three hours, so most callers set them explicitly. ## `GET /v1/machines/{machine_uuid}/signals/{public_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `machine_uuid` (string (uuid), required) — The machine's `uuid`, as returned by `GET /v1/machines`. - `public_id` (string (uuid), required) — The public ID. ### Query parameters - `start_time` (string (date-time), required) — Filter by start time. - `end_time` (string (date-time), required) — Filter by end time. - `agg_unit` ("s" | "m" | "h" | "d", optional, default `"s"`) — Filter by agg unit. - `agg_period` (integer, optional, default `1`, >= 1) — Filter by agg period. - `agg_function` ("avg" | "max" | "min" | "sum" | "count", optional, default `"avg"`) — Filter by agg function. - `transpose` (boolean, optional, default `false`) — Return parallel `timestamps` and `values` arrays instead of a list of `{timestamp, value}` points. ### Responses #### 200 — Aggregated points for the requested window: a `data` list of `{timestamp, value}` points, or parallel `timestamps` and `values` arrays when `transpose=true` One of 2 shapes — shape 1: object (`GetSignalDataResponseV1`) - `machine_uuid` (string (uuid), required, e.g. `"01748f7c-9688-7abc-8def-0123456789ab"`) — The machine's `uuid`, as returned by `GET /v1/machines`. - `public_id` (string (uuid), required) - `name` (string, required) - `agg_unit` ("s" | "m" | "h" | "d", required) - `agg_period` (integer, required) - `agg_function` ("avg" | "max" | "min" | "sum" | "count", required) - `data` (array of object, required) - `timestamp` (string (date-time), required) - `value` (number, required) One of 2 shapes — shape 2: object (`GetTransposedSignalDataResponseV1`) - `machine_uuid` (string (uuid), required, e.g. `"01748f7c-9688-7abc-8def-0123456789ab"`) — The machine's `uuid`, as returned by `GET /v1/machines`. - `public_id` (string (uuid), required) - `name` (string, required) - `agg_unit` ("s" | "m" | "h" | "d", required) - `agg_period` (integer, required) - `agg_function` ("avg" | "max" | "min" | "sum" | "count", required) - `timestamps` (array of string (date-time), required) — Bucket start times, in ascending order. - `values` (array of number, required) — Aggregated value of each bucket; `values[i]` belongs to `timestamps[i]`. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X GET 'https://draco.prod.pulsarml.com/v1/machines//signals/?start_time=2026-01-01T00%3A00%3A00Z&end_time=2026-01-02T00%3A00%3A00Z' \ -H 'x-api-key: ' ``` # List a machine's signals (https://docs.pulsarml.com/v1/api-reference/signals/get-machines-machine_uuid-signals) Return the raw sensor signals a machine exposes, with the `public_id` needed to read samples. A signal is one sensor channel read by the machine, named after the physical quantity it carries (`current`, `count`, `product_count`, `cycle`, `energy`, `temperature`, `pressure`, ...). A machine can expose several signals with the same `name`; their `display_name` tells them apart. Soft-deleted signals and signals without a kind are not listed. `description` is in English by default; pass `language=es` for Spanish. A description with no translation in the requested language is returned in English. ## `GET /v1/machines/{machine_uuid}/signals` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `machine_uuid` (string (uuid), required) — The machine's `uuid`, as returned by `GET /v1/machines`. ### Query parameters - `language` ("en" | "es", optional, default `"en"`) — Language of each signal's `description`. Falls back to English. ### Responses #### 200 — The raw sensor signals exposed for this machine Type: object (`GetMachineSignalsResponseV1`) - `machine_uuid` (string (uuid), required, e.g. `"01748f7c-9688-7abc-8def-0123456789ab"`) — The machine's `uuid`, as returned by `GET /v1/machines`. - `signals` (array of object, required) - `public_id` (string (uuid), required) — Identifier accepted by the signal data endpoint. - `name` (string, required) — Kind of physical quantity, for example `current`, `count` or `temperature`. - `display_name` (string, optional, nullable) — Label that tells apart two signals of the same kind on one machine. - `description` (string, optional, nullable) — What the signal measures, in the requested `language` (English if unavailable). #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X GET 'https://draco.prod.pulsarml.com/v1/machines//signals' \ -H 'x-api-key: ' ``` # Delete a SKU (https://docs.pulsarml.com/v1/api-reference/skus/delete-skus-sku_id) Delete a SKU from the catalog. Production logs and nominal speeds that still reference it are left pointing at a record that no longer resolves. Setting `archived` to `true` hides a product from pickers while keeping its history intact. ## `DELETE /v1/skus/{sku_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `sku_id` (string (uuid4), required) — The SKU ID. ### Responses #### 204 — Successful Response #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X DELETE 'https://draco.prod.pulsarml.com/v1/skus/' \ -H 'x-api-key: ' ``` # Retrieve a SKU (https://docs.pulsarml.com/v1/api-reference/skus/get-skus-sku_id) Return one SKU by its UUID. ## `GET /v1/skus/{sku_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `sku_id` (string (uuid4), required) — The SKU ID. ### Responses #### 200 — The SKU Type: object (`GetSkuByIdResponseV1`) - `id` (string (uuid4), required, e.g. `"36f7b519-d93e-4ec8-8245-7f4b8c123456"`) — The unique identifier for the sku. - `company` (object, required) — The company associated with the sku. - `id` (integer, required, e.g. `1`) — The unique identifier for the company. - `name` (string, required, e.g. `"company 1"`) — The name of the company. - `sku` (string, required, e.g. `"sku 1"`) — The sku code of the sku. - `name` (string, required, nullable, e.g. `"sku 1"`) — The name of the sku. - `description` (string, required, nullable, e.g. `"sku 1"`) — The description of the sku. - `unit` (string, required, nullable, e.g. `"pcs"`) — The unit of the sku. - `archived` (boolean, required, e.g. `false`) — The archived status of the sku. - `createdAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the sku was created. - `updatedAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the sku was last updated. - `deletedAt` (string (date-time), optional, nullable, e.g. `"2024-01-02T00:00:00.000Z"`) — The timestamp when the sku was deleted, if applicable. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X GET 'https://draco.prod.pulsarml.com/v1/skus/' \ -H 'x-api-key: ' ``` # Update a SKU (https://docs.pulsarml.com/v1/api-reference/skus/patch-skus-sku_id) Partially update a SKU: send only the fields that change. Changing `unit` does not convert historical counts — production logs keep the numbers they were written with. Archiving the SKU and creating a new one keeps its history consistent. ## `PATCH /v1/skus/{sku_id}` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Path parameters - `sku_id` (string (uuid4), required) — The SKU ID. ### Request body (`application/json`, required) Type: object (`UpdateSkuByIdRequestV1`) - `sku` (string, optional, nullable) — The code of the sku. - `name` (string, optional, nullable) — The name of the sku. - `description` (string, optional, nullable) — The description of the sku. - `unit` (string, optional, nullable) — The unit of the sku. - `archived` (boolean, optional, nullable, default `false`) — The archived status of the sku. ### Responses #### 204 — Successful Response #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X PATCH 'https://draco.prod.pulsarml.com/v1/skus/' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{}' ``` # Search SKUs (https://docs.pulsarml.com/v1/api-reference/skus/post-skus-filters) Search the product catalog, or page through it with `{}`. The filters are separate rather than one combined search: `skus` matches the SKU code and also accepts `include` / `exclude` by id, while `names`, `description` and `unit` are each their own fuzzy filter. Archived SKUs are part of the catalog and are not excluded automatically. ## `POST /v1/skus/filters` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Request body (`application/json`, required) Type: object (`GetSkusByFiltersRequestV1`) - `page` (integer, optional, default `1`, > 0, e.g. `1`) — The page number for pagination - `limit` (integer, optional, default `10`, > 0, e.g. `10`) — The number of items per page for pagination - `skus` (object, optional, nullable) — Filter criteria for SKUs - `search` (string, optional, nullable) — Search term for Skus - `include` (array of string, optional, nullable) — Items to include by id - `exclude` (array of string, optional, nullable) — Items to exclude by id - `names` (object, optional, nullable) — Filter criteria for names - `search` (string, optional, nullable) — Search term - `description` (object, optional, nullable) — Filter criteria for description - `search` (string, optional, nullable) — Search term - `unit` (object, optional, nullable) — Filter criteria for unit - `search` (string, optional, nullable) — Search term ### Responses #### 200 — A page of SKUs, with the total count and page count Type: object (`GetSkusByFiltersResponseV1`) - `totalCount` (integer, optional, default `100`) — The total count of items - `totalPages` (integer, optional, default `10`) — The total number of pages - `currentPage` (integer, optional, default `1`) — The current page - `skus` (array of object, required) - `id` (string (uuid4), required, e.g. `"36f7b519-d93e-4ec8-8245-7f4b8c123456"`) — The unique identifier for the sku. - `company` (object, required) — The company associated with the sku. - `id` (integer, required, e.g. `1`) — The unique identifier for the company. - `name` (string, required, e.g. `"company 1"`) — The name of the company. - `sku` (string, required, e.g. `"sku 1"`) — The sku code of the sku. - `name` (string, required, nullable, e.g. `"sku 1"`) — The name of the sku. - `description` (string, required, nullable, e.g. `"sku 1"`) — The description of the sku. - `unit` (string, required, nullable, e.g. `"pcs"`) — The unit of the sku. - `archived` (boolean, required, e.g. `false`) — The archived status of the sku. - `createdAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the sku was created. - `updatedAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the sku was last updated. - `deletedAt` (string (date-time), optional, nullable, e.g. `"2024-01-02T00:00:00.000Z"`) — The timestamp when the sku was deleted, if applicable. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/skus/filters' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{ "page": 1, "limit": 10, "skus": { "exclude": [ "9ce2c6b4-a1f4-4bf2-b5c8-10436d1018cc" ], "include": [ "9d02ab43-0100-4af6-a73c-e05abbd72648" ], "search": "sku 1" }, "names": { "search": "sku 1" }, "description": { "search": "a simple description" }, "unit": { "search": "kgs" } }' ``` # Create SKUs (https://docs.pulsarml.com/v1/api-reference/skus/post-skus) Create one or many SKUs. The body is a JSON **array**, even for a single record. Only `sku` (the product code) is required; `unit` defaults to `units` and `archived` to `false`. ## `POST /v1/skus` Base URLs: - `https://draco.prod.pulsarml.com` — Production - `https://draco.dev.pulsarml.com` — Development (sandbox) Authentication: send your API key in the `x-api-key` header. ### Request body (`application/json`, required) Type: array of object - `sku` (string, required) — The code of the sku. - `name` (string, optional, nullable) — The name of the sku. - `description` (string, optional, nullable) — The description of the sku. - `unit` (string, optional, nullable, default `"units"`) — The unit of the sku. - `archived` (boolean, optional, nullable, default `false`) — The archived status of the sku. ### Responses #### 200 — The SKUs that were created Type: array of object - `id` (string (uuid4), required, e.g. `"36f7b519-d93e-4ec8-8245-7f4b8c123456"`) — The unique identifier for the sku. - `company` (object, required) — The company associated with the sku. - `id` (integer, required, e.g. `1`) — The unique identifier for the company. - `name` (string, required, e.g. `"company 1"`) — The name of the company. - `sku` (string, required, e.g. `"sku 1"`) — The sku code of the sku. - `name` (string, required, nullable, e.g. `"sku 1"`) — The name of the sku. - `description` (string, required, nullable, e.g. `"sku 1"`) — The description of the sku. - `unit` (string, required, nullable, e.g. `"pcs"`) — The unit of the sku. - `archived` (boolean, required, e.g. `false`) — The archived status of the sku. - `createdAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the sku was created. - `updatedAt` (string (date-time), required, e.g. `"2024-01-01T00:00:00.000Z"`) — The timestamp when the sku was last updated. - `deletedAt` (string (date-time), optional, nullable, e.g. `"2024-01-02T00:00:00.000Z"`) — The timestamp when the sku was deleted, if applicable. #### 422 — Validation Error Type: object (`HTTPValidationError`) - `detail` (array of object, optional) - `loc` (array of string | integer, required) - `msg` (string, required) - `type` (string, required) ### Example request ```bash curl -X POST 'https://draco.prod.pulsarml.com/v1/skus' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '[ { "sku": "string" } ]' ``` # Classification created (https://docs.pulsarml.com/v1/api-reference/webhooks/machine-stop-classification-created) Sent when a machine stop is classified for the first time. ## Webhook: `MachineStopClassificationCreated` Pulsar sends a `POST` request with this body to the HTTPS endpoint you registered for the `MachineStopClassificationCreated` event. Verify `X-Pulsar-Signature` before trusting it; see https://docs.pulsarml.com/webhooks/signatures. ### Header parameters - `X-Pulsar-Signature` (string, required, e.g. `"5c0e2f4a1b9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f"`) — HMAC-SHA256 signature of the raw body. Verify it before trusting the request; see Verify signatures. - `X-Pulsar-Event-Type` (string, required, e.g. `"MachineStopClassificationCreated"`) — The event type, same as `eventType` in the body. - `X-Pulsar-Event-Id` (string, required, e.g. `"8d2f6c1e-4b7a-4f0e-9a3c-5e1d7b9f2a64"`) — The event's ID, same as `eventId` in the body. - `X-Pulsar-Timestamp` (string, required, e.g. `"2026-09-25T14:03:12.482Z"`) — Same as `eventTimestamp` in the body. - `X-Pulsar-Request-Id` (string, required, e.g. `"3f9b2c7d-1e4a-4c8b-b6d2-9a0e5f7c1b38"`) — The ID of this delivery attempt. Include it when you contact Pulsar support about a delivery. ### Request body (`application/json`, required) Type: object - `eventId` (string (uuid), required) — Unique ID of the event. It stays the same across retries, so use it to detect duplicates. - `eventTimestamp` (string (date-time), required) — When Pulsar queued the event, as an ISO 8601 UTC timestamp. This is not the time the change happened in Pulsar. - `eventType` ("MachineStopClassificationCreated", required) — The event type. - `event` (object, required) — A machine stop and its classification. A stop is identified by its `machine` and its `start`. - `machine` (string, required, e.g. `"Injection molder 4"`) — The machine's name in Pulsar. - `machineExternalName` (string, required, nullable, e.g. `"INJ-004"`) — The machine's external name, for example its ID in your ERP. `null` if it has none. - `start` (string (date-time), required, e.g. `"2026-09-25T13:41:07.000Z"`) — When the stop started, as an ISO 8601 UTC timestamp. - `end` (string (date-time), required, nullable, e.g. `"2026-09-25T13:58:30.000Z"`) — When the stop ended, as an ISO 8601 UTC timestamp. Can be `null`. - `operator` (string, required, nullable, e.g. `"Ana Torres"`) — Full name of the operator on the machine when the stop started. `null` if nobody was logged in. - `generalCause` (string, required, nullable, e.g. `"Mechanical failure"`) — Name of the general cause. - `specificCause` (string, required, nullable, e.g. `"Mold jam"`) — Name of the specific cause. - `component` (string, required, nullable, e.g. `"Ejector pin"`) — Name of the component involved. - `description` (string, required, nullable, e.g. `"Part stuck in cavity 2, cleared manually"`) — Free-text description of the stop. - `isAutomated` (boolean, required, e.g. `false`) — `true` if the classification was applied automatically by a rule, `false` if a person entered it. ### Your response #### 200 — Return any `2XX` within 10 seconds to acknowledge the delivery; Pulsar ignores the response body. A timeout, `408`, `429` or `5XX` is retried. Any other `4XX`, or a `3XX` redirect, is not. ### Example body ```json { "eventId": "8d2f6c1e-4b7a-4f0e-9a3c-5e1d7b9f2a64", "eventTimestamp": "2026-09-25T14:03:12.482Z", "eventType": "MachineStopClassificationCreated", "event": { "machine": "Injection molder 4", "machineExternalName": "INJ-004", "start": "2026-09-25T13:41:07.000Z", "end": "2026-09-25T13:58:30.000Z", "operator": "Ana Torres", "generalCause": "Mechanical failure", "specificCause": "Mold jam", "component": "Ejector pin", "description": "Part stuck in cavity 2, cleared manually", "isAutomated": false } } ``` # Classification deleted (https://docs.pulsarml.com/v1/api-reference/webhooks/machine-stop-classification-deleted) Sent when a classification is removed, leaving the stop unclassified. The causes, component, description and `isAutomated` describe the classification as it was before it was deleted; the machine, stop times and operator come from Pulsar's current records. ## Webhook: `MachineStopClassificationDeleted` Pulsar sends a `POST` request with this body to the HTTPS endpoint you registered for the `MachineStopClassificationDeleted` event. Verify `X-Pulsar-Signature` before trusting it; see https://docs.pulsarml.com/webhooks/signatures. ### Header parameters - `X-Pulsar-Signature` (string, required, e.g. `"5c0e2f4a1b9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f"`) — HMAC-SHA256 signature of the raw body. Verify it before trusting the request; see Verify signatures. - `X-Pulsar-Event-Type` (string, required, e.g. `"MachineStopClassificationDeleted"`) — The event type, same as `eventType` in the body. - `X-Pulsar-Event-Id` (string, required, e.g. `"8d2f6c1e-4b7a-4f0e-9a3c-5e1d7b9f2a64"`) — The event's ID, same as `eventId` in the body. - `X-Pulsar-Timestamp` (string, required, e.g. `"2026-09-25T14:03:12.482Z"`) — Same as `eventTimestamp` in the body. - `X-Pulsar-Request-Id` (string, required, e.g. `"3f9b2c7d-1e4a-4c8b-b6d2-9a0e5f7c1b38"`) — The ID of this delivery attempt. Include it when you contact Pulsar support about a delivery. ### Request body (`application/json`, required) Type: object - `eventId` (string (uuid), required) — Unique ID of the event. It stays the same across retries, so use it to detect duplicates. - `eventTimestamp` (string (date-time), required) — When Pulsar queued the event, as an ISO 8601 UTC timestamp. This is not the time the change happened in Pulsar. - `eventType` ("MachineStopClassificationDeleted", required) — The event type. - `event` (object, required) — A machine stop and its classification. A stop is identified by its `machine` and its `start`. - `machine` (string, required, e.g. `"Injection molder 4"`) — The machine's name in Pulsar. - `machineExternalName` (string, required, nullable, e.g. `"INJ-004"`) — The machine's external name, for example its ID in your ERP. `null` if it has none. - `start` (string (date-time), required, e.g. `"2026-09-25T13:41:07.000Z"`) — When the stop started, as an ISO 8601 UTC timestamp. - `end` (string (date-time), required, nullable, e.g. `"2026-09-25T13:58:30.000Z"`) — When the stop ended, as an ISO 8601 UTC timestamp. Can be `null`. - `operator` (string, required, nullable, e.g. `"Ana Torres"`) — Full name of the operator on the machine when the stop started. `null` if nobody was logged in. - `generalCause` (string, required, nullable, e.g. `"Mechanical failure"`) — Name of the general cause. - `specificCause` (string, required, nullable, e.g. `"Mold jam"`) — Name of the specific cause. - `component` (string, required, nullable, e.g. `"Ejector pin"`) — Name of the component involved. - `description` (string, required, nullable, e.g. `"Part stuck in cavity 2, cleared manually"`) — Free-text description of the stop. - `isAutomated` (boolean, required, e.g. `false`) — `true` if the classification was applied automatically by a rule, `false` if a person entered it. ### Your response #### 200 — Return any `2XX` within 10 seconds to acknowledge the delivery; Pulsar ignores the response body. A timeout, `408`, `429` or `5XX` is retried. Any other `4XX`, or a `3XX` redirect, is not. ### Example body ```json { "eventId": "5e93b0d7-8a14-4c6f-b2e8-1d7f0a3c9b56", "eventTimestamp": "2026-09-25T15:02:09.804Z", "eventType": "MachineStopClassificationDeleted", "event": { "machine": "Injection molder 4", "machineExternalName": "INJ-004", "start": "2026-09-25T13:41:07.000Z", "end": "2026-09-25T13:58:30.000Z", "operator": "Ana Torres", "generalCause": "Mechanical failure", "specificCause": "Ejector failure", "component": "Ejector pin", "description": "Ejector pin bent, replaced", "isAutomated": false } } ``` # Classification updated (https://docs.pulsarml.com/v1/api-reference/webhooks/machine-stop-classification-updated) Sent when an existing classification changes, for example when someone corrects the cause. The `event` object carries the classification's current values. ## Webhook: `MachineStopClassificationUpdated` Pulsar sends a `POST` request with this body to the HTTPS endpoint you registered for the `MachineStopClassificationUpdated` event. Verify `X-Pulsar-Signature` before trusting it; see https://docs.pulsarml.com/webhooks/signatures. ### Header parameters - `X-Pulsar-Signature` (string, required, e.g. `"5c0e2f4a1b9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f"`) — HMAC-SHA256 signature of the raw body. Verify it before trusting the request; see Verify signatures. - `X-Pulsar-Event-Type` (string, required, e.g. `"MachineStopClassificationUpdated"`) — The event type, same as `eventType` in the body. - `X-Pulsar-Event-Id` (string, required, e.g. `"8d2f6c1e-4b7a-4f0e-9a3c-5e1d7b9f2a64"`) — The event's ID, same as `eventId` in the body. - `X-Pulsar-Timestamp` (string, required, e.g. `"2026-09-25T14:03:12.482Z"`) — Same as `eventTimestamp` in the body. - `X-Pulsar-Request-Id` (string, required, e.g. `"3f9b2c7d-1e4a-4c8b-b6d2-9a0e5f7c1b38"`) — The ID of this delivery attempt. Include it when you contact Pulsar support about a delivery. ### Request body (`application/json`, required) Type: object - `eventId` (string (uuid), required) — Unique ID of the event. It stays the same across retries, so use it to detect duplicates. - `eventTimestamp` (string (date-time), required) — When Pulsar queued the event, as an ISO 8601 UTC timestamp. This is not the time the change happened in Pulsar. - `eventType` ("MachineStopClassificationUpdated", required) — The event type. - `event` (object, required) — A machine stop and its classification. A stop is identified by its `machine` and its `start`. - `machine` (string, required, e.g. `"Injection molder 4"`) — The machine's name in Pulsar. - `machineExternalName` (string, required, nullable, e.g. `"INJ-004"`) — The machine's external name, for example its ID in your ERP. `null` if it has none. - `start` (string (date-time), required, e.g. `"2026-09-25T13:41:07.000Z"`) — When the stop started, as an ISO 8601 UTC timestamp. - `end` (string (date-time), required, nullable, e.g. `"2026-09-25T13:58:30.000Z"`) — When the stop ended, as an ISO 8601 UTC timestamp. Can be `null`. - `operator` (string, required, nullable, e.g. `"Ana Torres"`) — Full name of the operator on the machine when the stop started. `null` if nobody was logged in. - `generalCause` (string, required, nullable, e.g. `"Mechanical failure"`) — Name of the general cause. - `specificCause` (string, required, nullable, e.g. `"Mold jam"`) — Name of the specific cause. - `component` (string, required, nullable, e.g. `"Ejector pin"`) — Name of the component involved. - `description` (string, required, nullable, e.g. `"Part stuck in cavity 2, cleared manually"`) — Free-text description of the stop. - `isAutomated` (boolean, required, e.g. `false`) — `true` if the classification was applied automatically by a rule, `false` if a person entered it. ### Your response #### 200 — Return any `2XX` within 10 seconds to acknowledge the delivery; Pulsar ignores the response body. A timeout, `408`, `429` or `5XX` is retried. Any other `4XX`, or a `3XX` redirect, is not. ### Example body ```json { "eventId": "c41a7e09-2d6b-4f38-9e15-7b0c3a9d4f22", "eventTimestamp": "2026-09-25T14:20:45.117Z", "eventType": "MachineStopClassificationUpdated", "event": { "machine": "Injection molder 4", "machineExternalName": "INJ-004", "start": "2026-09-25T13:41:07.000Z", "end": "2026-09-25T13:58:30.000Z", "operator": "Ana Torres", "generalCause": "Mechanical failure", "specificCause": "Ejector failure", "component": "Ejector pin", "description": "Ejector pin bent, replaced", "isAutomated": false } } ``` # Ping (https://docs.pulsarml.com/v1/api-reference/webhooks/ping) Sent when Pulsar tests your endpoint. It can reach an endpoint whatever event type it's registered for, so reply `2XX` to it rather than rejecting event types you didn't expect. ## Webhook: `Ping` Pulsar sends a `POST` request with this body to the HTTPS endpoint you registered for the `Ping` event. Verify `X-Pulsar-Signature` before trusting it; see https://docs.pulsarml.com/webhooks/signatures. ### Header parameters - `X-Pulsar-Signature` (string, required, e.g. `"5c0e2f4a1b9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f"`) — HMAC-SHA256 signature of the raw body. Verify it before trusting the request; see Verify signatures. - `X-Pulsar-Event-Type` (string, required, e.g. `"Ping"`) — The event type, same as `eventType` in the body. - `X-Pulsar-Event-Id` (string, required, e.g. `"8d2f6c1e-4b7a-4f0e-9a3c-5e1d7b9f2a64"`) — The event's ID, same as `eventId` in the body. - `X-Pulsar-Timestamp` (string, required, e.g. `"2026-09-25T14:03:12.482Z"`) — Same as `eventTimestamp` in the body. - `X-Pulsar-Request-Id` (string, required, e.g. `"3f9b2c7d-1e4a-4c8b-b6d2-9a0e5f7c1b38"`) — The ID of this delivery attempt. Include it when you contact Pulsar support about a delivery. ### Request body (`application/json`, required) Type: object - `eventId` (string (uuid), required) — Unique ID of the event. It stays the same across retries, so use it to detect duplicates. - `eventTimestamp` (string (date-time), required) — When Pulsar queued the event, as an ISO 8601 UTC timestamp. This is not the time the change happened in Pulsar. - `eventType` ("Ping", required) — The event type. - `event` (object, required) - `text` ("ping", required) — Always `ping`. ### Your response #### 200 — Return any `2XX` within 10 seconds to acknowledge the delivery; Pulsar ignores the response body. A timeout, `408`, `429` or `5XX` is retried. Any other `4XX`, or a `3XX` redirect, is not. ### Example body ```json { "eventId": "0b6f3d2a-9c1e-4a7b-8f5d-2e4c6a8b0d1f", "eventTimestamp": "2026-09-25T14:00:00.000Z", "eventType": "Ping", "event": { "text": "ping" } } ```