Two kinds of credential
Management calls and event ingestion use different credentials, deliberately. A terminal in the field only ever holds a key that can post its own events — never one that could provision more terminals or read your estate.
1. Get a token
Create an organisation API key in the dashboard under Developer settings, then exchange it for a short-lived JWT.
curl -X POST https://pulse-grid.dev/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{ "apiKey": "sk_live_..." }'
# → { "accessToken": "eyJ...", "expiresIn": 3600 }
2. Create a location
A location is a site, store or depot. Skip this if you are adding terminals to one that already exists.
curl -X POST https://pulse-grid.dev/api/v1/locations \
-H "Authorization: Bearer eyJ..." \
-H "Content-Type: application/json" \
-d '{ "name": "Riverside Retail Park", "city": "Manchester" }'
3. Provision terminals
Register your devices in one call. Each comes back with its own ingest key — save them, they are what the devices will authenticate with. Terminals whose externalId already exists are skipped, so this is safe to re-run.
curl -X POST https://pulse-grid.dev/api/v1/terminals/bulk \
-H "Authorization: Bearer eyJ..." \
-H "Content-Type: application/json" \
-d '{
"defaultLocationId": "<location-id>",
"terminals": [
{ "externalId": "BAY-01", "name": "Wash bay 1" },
{ "externalId": "BAY-02", "name": "Wash bay 2" }
]
}'
# → one object per terminal, each with its own "apiKey": "pk_live_..."
4. Post a transaction
Send a transaction whenever the device takes money. occurredAt is what analytics are built from, so a device that buffered while offline still reports into the right period.
curl -X POST https://pulse-grid.dev/api/v1/events/transaction \
-H "X-Api-Key: pk_live_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 6.50,
"currency": "EUR",
"status": "Success",
"paymentType": "card",
"productName": "Premium wash",
"quantity": 1,
"occurredAt": "2026-07-22T14:05:11Z"
}'
There is no field for a card number, cardholder name, expiry or CVV, and the API will not accept them. PulseGrid records that a payment happened, not its contents.
5. Send heartbeats
Heartbeats are how PulseGrid knows a device is alive. If one has not checked in within the window you set, it flips to offline and alerting fires.
curl -X POST https://pulse-grid.dev/api/v1/events/heartbeat \
-H "X-Api-Key: pk_live_..." \
-H "Content-Type: application/json" \
-d '{
"occurredAt": "2026-07-22T14:05:00Z",
"reportedStatus": "Online",
"firmwareVersion": "2.4.1"
}'
Report errors
Errors carry your own codes, so a bill validator jam reads as a bill validator jam rather than a generic failure. Severity is Info, Warning or Critical.
curl -X POST https://pulse-grid.dev/api/v1/events/error \
-H "X-Api-Key: pk_live_..." \
-H "Content-Type: application/json" \
-d '{
"code": "BILL_VALIDATOR_JAM",
"message": "Note path obstructed",
"severity": "Critical",
"occurredAt": "2026-07-22T14:06:20Z"
}'
Batching
Devices on an intermittent connection should buffer locally and flush through POST /api/v1/events/batch, which takes multiple events in one request. Because each event carries its own timestamp, a flush after an outage lands correctly in history instead of bunching at the moment it arrived.
Common questions
Which credential do I use where?
Two, and they are not interchangeable. An organisation API key (sk_live_…) is exchanged for a short-lived JWT and used for management calls — creating locations and provisioning terminals. Each terminal then gets its own ingest key (pk_live_…) sent as X-Api-Key, and that key can only post events for that terminal.
Do I need one request per event?
No. POST /api/v1/events/batch accepts multiple events in a single call, which is what you want for devices that buffer while offline and flush on reconnect.
What happens to events recorded while a device was offline?
Send them when the device reconnects. Every event carries its own occurredAt timestamp, which is what analytics and history are built from — the separate received-at time is recorded but does not affect where the event lands in your reporting.
Is provisioning safe to run more than once?
Yes. POST /api/v1/terminals/bulk silently skips terminals whose externalId already exists, so you can run it repeatedly from a deploy script without creating duplicates.
I lost a terminal key. Do I have to re-provision?
No. GET /api/v1/terminals and GET /api/v1/terminals/lookup return each terminal’s apiKey, not just the creation response.
Can I test without hardware?
Yes. The developer console in the dashboard includes a simulator that sends real heartbeats and payment events through the same API, plus a live event feed showing exactly what arrived.
What decides whether a terminal shows as offline?
A heartbeat window you set for your organisation. If a terminal has not checked in within it, it flips to offline and alerting fires. It is configurable because a device reporting every 30 seconds and one reporting every 10 minutes are both perfectly healthy.