Connect API API reference

Connect your systems to Spillard Live

The Spillard Connect API gives your software the vehicles, drivers, devices, events, journeys and video that Spillard Live records. Call the REST API when you need data, and let signed webhooks tell you the moment something happens on the road.

Your organization’s administrator creates them in Spillard Live: SettingsOrganizationAPI access.

Base URL
https://connect.spillard.live
Authentication
OAuth2 client credentials
Errors
RFC 9457 problem details
Specification
OpenAPI 3.1
Webhooks
CloudEvents 1.0, signed
Webhook deliveryHarsh braking, A40 Ealing

POST /webhooks/spillard HTTP/1.1

content-type:
application/cloudevents+json
webhook-id:
8e2d4a71-c63f-5b09-9d4e-1a7f6c2b8e35
webhook-timestamp:
1790667681
webhook-signature:
v1,RIY+drZnGh+uVtGLRWiQYzjIwH81O/JCZ8er1aL02TY=
{
  "type": "spillard.event.raised.v1",
  "data": {
    "eventTypes": [
      "driver_behaviour.harsh_braking"
    ],
    "speedKph": 54,
    "speedLimitKph": 64,
    "mediaExpected": true,
    …
  }
}
Signature verified Verify it yourself

From credentials to your first call in three steps

Authenticate with the OAuth2 client credentials grant, then call any endpoint with the bearer token. The organizations your client may read come from the token, so there is no organization header to manage.

  1. Get credentials

    An administrator of your organization creates the API client in Spillard Live, under SettingsOrganizationAPI access, with the scopes your integration needs. You receive a client_id and a client_secret; the secret is shown once, so store it in your secret store. Not an administrator? Ask yours for a client with the scopes you need, for example vehicles.read and fleets.read.

    Access to several organizations, or to selected fleets only, is set up by Spillard on request through your Spillard account manager. A client holding api-clients.write can create and rotate further clients itself through /v1/api-clients.

    Your credentials
    export SPILLARD_CLIENT_ID='<client id>'
    # paste it: hidden, kept out of history
    read -rs SPILLARD_CLIENT_SECRET
    export SPILLARD_CLIENT_SECRET
  2. Get a token

    Exchange the credentials at Spillard Identity for a bearer token. Ask for the scopes you need, each in the form <resource>.<access>; the token’s audience is https://connect.spillard.live. Request a new token when expires_in runs out. The command keeps the token in TOKEN with jq.

    POST identity.spillard.live/connect/token
    TOKEN=$(curl -sS https://identity.spillard.live/connect/token \
      -d grant_type=client_credentials \
      --data-urlencode "client_id=${SPILLARD_CLIENT_ID}" \
      --data-urlencode "client_secret=${SPILLARD_CLIENT_SECRET}" \
      --data-urlencode "scope=vehicles.read fleets.read" | jq -r .access_token)
    The token endpoint answers (the command keeps access_token in TOKEN)
    {
      "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6…",
      "expires_in": 3600,
      "token_type": "Bearer",
      "scope": "fleets.read vehicles.read"
    }
  3. Make your first call

    Send the access_token as a bearer header, and Accept-Encoding: br, gzip to receive compressed responses (curl’s --compressed asks for compression and unpacks the answer). The organizations your client may read are resolved from the token; GET /v1/context accepts any valid token and shows them in access, with the scopes your calls act on. Collections return items with an opaque nextCursor.

    GET connect.spillard.live/v1/vehicles
    curl -sS --compressed 'https://connect.spillard.live/v1/vehicles?limit=1' \
      -H "Authorization: Bearer ${TOKEN}"
    Response
    {
      "items": [
        {
          "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
          "registrationNumber": "SL24 WHK",
          "make": "Ford",
          "model": "Transit",
          "chassisNumber": null,
          "organization": { "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14" },
          "fleet": { "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35" },
          "driver": { "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945" }
        }
      ],
      "hasMore": true,
      "nextCursor": "djE6…",
      "totalCount": null
    }

Every resource, and the scope it needs

Each tile opens its section of the API reference, with schemas and a try-it console. Reads need <resource>.read, changes need <resource>.write; the reference names the required scope on every operation.

Scopes. Request the least your integration needs. Device and journey event lists, and the metrics, need events.read; an event's share link needs media.read too. The tiles list every scope there is, and the reference describes what each one allows.

Retries and updates. Creates and device actions that list an Idempotency-Key header in the reference are safe to retry with one, and PATCH updates of vehicles and drivers require If-Match with the resource’s ETag.

Six types, one envelope, one signature

Register an HTTPS endpoint and Spillard posts a signed JSON body when something happens in your organization, or, for an endpoint with includeSubOrganizations, in an organization below it.

Every body is a CloudEvents 1.0 event in structured mode. The signature headers follow Standard Webhooks, so any Standard Webhooks library verifies them.

id
The message id; the same on every retry
type
spillard.<area>.<fact>.v1
source
/organizations/{id}, the organization the event belongs to
subject
The resource; not on telemetry batches
time
When it happened
data
The per-type body
  • Verify every requestwebhook-signature is v1,<base64 HMAC-SHA256> of {webhook-id}.{webhook-timestamp}.{body}, keyed with your endpoint’s whsec_… signing secret. During a secret rotation the header carries one signature per active secret, current first: accept the message if any signature verifies with any secret you hold, and keep the previous secret until previousSecretExpiresAt.
  • Answer 2xx within 10 sAnything else is retried with backoff for 24 h (telemetry.history 72 h), then dead-lettered; telemetry.live is retried for 60 s and then dropped. At most 256 KB per request.
  • At least once, in any orderDeduplicate on webhook-id, which stays the same on a retry. Delivery order is not guaranteed, not even for one device: the current state comes from revision and replaces (journeys), completedAt (media requests) and recordedAt (live positions).
POST /webhooks/spillard HTTP/1.1
host: hooks.example.com
content-type: application/cloudevents+json; charset=utf-8
user-agent: Spillard-Webhooks/1
webhook-id: 8e2d4a71-c63f-5b09-9d4e-1a7f6c2b8e35
webhook-timestamp: 1790667681
webhook-signature: v1,RIY+drZnGh+uVtGLRWiQYzjIwH81O/JCZ8er1aL02TY=

{ …body: the event.raised example below… }
The signature is real: HMAC-SHA256 over webhook-id.webhook-timestamp.body with the test secret whsec_c3BpbGxhcmQtZXhhbXBsZS1zZWNyZXQtMDE=, where the body is the event.raised example below exactly as shown, with a trailing newline. A Standard Webhooks library verifies it once its timestamp tolerance check is disabled.
Verify the signature yourself
# save event.raised.v1.json first (Copy button above)
python3 - event.raised.v1.json <<'EOF'
import base64, hashlib, hmac, sys
# the test secret, without its whsec_ prefix
key = base64.b64decode("c3BpbGxhcmQtZXhhbXBsZS1zZWNyZXQtMDE=")
msg = b"8e2d4a71-c63f-5b09-9d4e-1a7f6c2b8e35.1790667681."
msg += open(sys.argv[1], "rb").read()
mac = hmac.new(key, msg, hashlib.sha256).digest()
print("v1," + base64.b64encode(mac).decode())
EOF
Prints
v1,RIY+drZnGh+uVtGLRWiQYzjIwH81O/JCZ8er1aL02TY=

Set up delivery

  1. Create an endpointPOST /v1/webhook-endpoints with your HTTPS url and the eventTypes it takes (webhooks.write). The response carries the signingSecret once. includeSubOrganizations adds the events of the organizations below yours; it is on by default when your client may read them.
  2. Send a testPOST /v1/webhook-endpoints/{endpointId}:test with an eventType sends one signed test message and reports the outcome.
  3. Switch delivery onPUT /v1/webhook-settings with {"enabled": true} (webhook-settings.write). Nothing is delivered while it is off.
  4. Watch and replayGET /v1/webhook-endpoints/{endpointId}/deliveries lists pending and failed deliveries; …/deliveries/{deliveryId}:replay queues a failed one again.

The six types

alarm.raised

spillard.alarm.raised.v1

Retries
24 h
Delivery
Opt-in

A device alarm passed alarm processing and was not discarded. kind classifies it (io_input, emergency, dsm, adas, acceleration, user_defined); eventId is the event that processing created, or null. Opt-in: an alarm that creates an event also arrives as event.raised, so subscribe to this type only when you also need the alarms that created no event.

Example bodyalarm.raised.v1.json
alarm.raised.v1.json
{
  "specversion": "1.0",
  "id": "0b7c3e9a-51d4-5f28-a6c1-9e3d7f0b2a64",
  "type": "spillard.alarm.raised.v1",
  "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
  "subject": "alarms/4d2f8a61-9c7e-4b15-83d0-a6e5f1c2b978",
  "time": "2026-09-29T07:12:55.000Z",
  "datacontenttype": "application/json",
  "data": {
    "alarmId": "4d2f8a61-9c7e-4b15-83d0-a6e5f1c2b978",
    "triggeredAt": "2026-09-29T07:12:55.000Z",
    "kind": "io_input",
    "input": 2,
    "vendorKey": "ME41_ALARM_IO_ALARM2",
    "location": {
      "latitude": 51.53184,
      "longitude": -0.262915
    },
    "speedKph": 0,
    "headingDegrees": 90,
    "eventId": null,
    "organization": {
      "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
    },
    "fleet": {
      "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
    },
    "vehicle": {
      "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
      "registration": "SL24 WHK"
    },
    "driver": {
      "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
    },
    "device": {
      "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
      "hardwareId": "009a2c41f7"
    }
  }
}

event.raised

spillard.event.raised.v1

Retries
24 h

A device event was stored. eventTypes carries family.leaf tokens (for example driver_behaviour.harsh_braking), which an endpoint can filter on with eventTypeFilters. links.self and links.media point to /v1/events/{eventId} and its media.

Example bodyevent.raised.v1.json
event.raised.v1.json
{
  "specversion": "1.0",
  "id": "8e2d4a71-c63f-5b09-9d4e-1a7f6c2b8e35",
  "type": "spillard.event.raised.v1",
  "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
  "subject": "events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
  "time": "2026-09-29T07:41:17.000Z",
  "datacontenttype": "application/json",
  "data": {
    "eventId": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
    "triggeredAt": "2026-09-29T07:41:17.000Z",
    "receivedAt": "2026-09-29T07:41:19.412Z",
    "eventTypes": [
      "driver_behaviour.harsh_braking"
    ],
    "classification": "medium",
    "origin": "device",
    "location": {
      "latitude": 51.530112,
      "longitude": -0.292731
    },
    "address": {
      "label": "Western Avenue (A40), Ealing, London, United Kingdom",
      "street": "Western Avenue",
      "city": "London",
      "postalCode": null,
      "countryCode": "GBR"
    },
    "speedKph": 54,
    "maxSpeedKph": 57,
    "speedLimitKph": 64,
    "headingDegrees": 263,
    "mediaExpected": true,
    "firmwareVersion": "T250922.01",
    "organization": {
      "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
    },
    "fleet": {
      "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
    },
    "vehicle": {
      "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
      "registration": "SL24 WHK"
    },
    "driver": {
      "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
    },
    "device": {
      "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
      "hardwareId": "009a2c41f7"
    },
    "links": {
      "self": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
      "media": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53/media"
    }
  }
}

journey.completed

spillard.journey.completed.v1

Retries
24 h

A journey was closed, extended or reprocessed, with distance, durations, start and end. The latest revision wins; delete the journeys listed in replaces. There is no driver in v1: Spillard does not know who drove a journey.

Example bodyjourney.completed.v1.json
journey.completed.v1.json
{
  "specversion": "1.0",
  "id": "5a9f1c3e-7d2b-5e84-b6a0-c3e8d1f7a492",
  "type": "spillard.journey.completed.v1",
  "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
  "subject": "journeys/20260929T065804000Z009a2c41f7",
  "time": "2026-09-29T08:02:40.000Z",
  "datacontenttype": "application/json",
  "data": {
    "journeyId": "20260929T065804000Z009a2c41f7",
    "revision": 1,
    "replaces": [],
    "startedAt": "2026-09-29T06:58:04.000Z",
    "endedAt": "2026-09-29T08:02:40.000Z",
    "start": {
      "location": {
        "latitude": 51.532704,
        "longitude": -0.268213
      },
      "address": {
        "label": "Coronation Road, Park Royal, London, United Kingdom",
        "street": "Coronation Road",
        "city": "London",
        "postalCode": null,
        "countryCode": "GBR"
      }
    },
    "end": {
      "location": {
        "latitude": 51.481022,
        "longitude": -0.429587
      },
      "address": {
        "label": "Bath Road, Harlington, Hayes, United Kingdom",
        "street": "Bath Road",
        "city": "Hayes",
        "postalCode": null,
        "countryCode": "GBR"
      }
    },
    "distanceMeters": 21480,
    "durationSeconds": 3876,
    "movingSeconds": 3102,
    "idleSeconds": 774,
    "ignitionOnSeconds": 3876,
    "maxSpeedKph": 81,
    "speedingCount": 2,
    "gpsCoverage": "full",
    "organization": {
      "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
    },
    "fleet": {
      "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
    },
    "vehicle": {
      "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
      "registration": "SL24 WHK"
    },
    "device": {
      "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
      "hardwareId": "009a2c41f7"
    }
  }
}

telemetry.live

spillard.telemetry.live.v1

Retries
60 s

The latest positions and ignition state of your organization’s vehicles, batched every few seconds with many vehicles in one request. The latest recordedAt per vehicle wins; stale positions are not retried.

Example bodytelemetry.live.v1.json
telemetry.live.v1.json
{
  "specversion": "1.0",
  "id": "d3a7e1f9-4b6c-5d82-a9e3-6f1c0b8d4e27",
  "type": "spillard.telemetry.live.v1",
  "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
  "time": "2026-09-29T07:41:18.000Z",
  "datacontenttype": "application/json",
  "data": {
    "positions": [
      {
        "recordedAt": "2026-09-29T07:41:18.000Z",
        "location": {
          "latitude": 51.530103,
          "longitude": -0.292884
        },
        "speedKph": 38,
        "headingDegrees": 263,
        "ignitionOn": true,
        "organization": {
          "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
        },
        "fleet": {
          "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
        },
        "vehicle": {
          "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
          "registration": "SL24 WHK"
        },
        "driver": {
          "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
        },
        "device": {
          "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
          "hardwareId": "009a2c41f7"
        }
      },
      {
        "recordedAt": "2026-09-29T07:41:16.000Z",
        "location": {
          "latitude": 51.532655,
          "longitude": -0.268391
        },
        "speedKph": 0,
        "headingDegrees": 182,
        "ignitionOn": false,
        "organization": {
          "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
        },
        "fleet": {
          "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
        },
        "vehicle": {
          "id": "5d8c2b7e-1a4f-4e93-8b6d-0f3a9c7e2d51",
          "registration": "SL24 WHN"
        },
        "driver": null,
        "device": {
          "id": "2c7e9a14-58b3-4f06-9d21-a4e8f3b6c051",
          "hardwareId": "009a2c4388"
        }
      }
    ]
  }
}

telemetry.history

spillard.telemetry.history.v1

Retries
72 h

Every raw GPS frame of your organization’s devices, batched as tracks, backlog included (backfill) when a device comes back online. Tracks carry no driver in v1.

Example bodytelemetry.history.v1.json
telemetry.history.v1.json
{
  "specversion": "1.0",
  "id": "71c4e8b2-3d9f-5a16-b8e7-4a2d6c9f0e53",
  "type": "spillard.telemetry.history.v1",
  "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
  "time": "2026-09-29T07:41:19.000Z",
  "datacontenttype": "application/json",
  "data": {
    "tracks": [
      {
        "windowStartAt": "2026-09-29T07:41:15.000Z",
        "windowEndAt": "2026-09-29T07:41:19.000Z",
        "backfill": false,
        "frameCount": 5,
        "frames": [
          {
            "recordedAt": "2026-09-29T07:41:15.000Z",
            "location": {
              "latitude": 51.53014,
              "longitude": -0.292299
            },
            "speedKph": 57,
            "headingDegrees": 263,
            "gpsValid": true,
            "ignitionOn": true
          },
          {
            "recordedAt": "2026-09-29T07:41:16.000Z",
            "location": {
              "latitude": 51.530126,
              "longitude": -0.292526
            },
            "speedKph": 56,
            "headingDegrees": 263,
            "gpsValid": true,
            "ignitionOn": true
          },
          {
            "recordedAt": "2026-09-29T07:41:17.000Z",
            "location": {
              "latitude": 51.530112,
              "longitude": -0.292731
            },
            "speedKph": 54,
            "headingDegrees": 263,
            "gpsValid": true,
            "ignitionOn": true
          },
          {
            "recordedAt": "2026-09-29T07:41:18.000Z",
            "location": {
              "latitude": 51.530103,
              "longitude": -0.292884
            },
            "speedKph": 38,
            "headingDegrees": 263,
            "gpsValid": true,
            "ignitionOn": true
          },
          {
            "recordedAt": "2026-09-29T07:41:19.000Z",
            "location": {
              "latitude": 51.530097,
              "longitude": -0.29298
            },
            "speedKph": 24,
            "headingDegrees": 263,
            "gpsValid": true,
            "ignitionOn": true
          }
        ],
        "organization": {
          "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
        },
        "fleet": {
          "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
        },
        "vehicle": {
          "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
          "registration": "SL24 WHK"
        },
        "device": {
          "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
          "hardwareId": "009a2c41f7"
        }
      },
      {
        "windowStartAt": "2026-09-29T05:12:00.000Z",
        "windowEndAt": "2026-09-29T05:12:02.000Z",
        "backfill": true,
        "frameCount": 3,
        "frames": [
          {
            "recordedAt": "2026-09-29T05:12:00.000Z",
            "location": {
              "latitude": 51.509812,
              "longitude": -0.378655
            },
            "speedKph": 0,
            "headingDegrees": 104,
            "gpsValid": true,
            "ignitionOn": false
          },
          {
            "recordedAt": "2026-09-29T05:12:01.000Z",
            "location": {
              "latitude": 51.509812,
              "longitude": -0.378655
            },
            "speedKph": 0,
            "headingDegrees": 104,
            "gpsValid": true,
            "ignitionOn": true
          },
          {
            "recordedAt": "2026-09-29T05:12:02.000Z",
            "location": {
              "latitude": 51.509808,
              "longitude": -0.378631
            },
            "speedKph": 6,
            "headingDegrees": 104,
            "gpsValid": true,
            "ignitionOn": true
          }
        ],
        "organization": {
          "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
        },
        "fleet": {
          "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
        },
        "vehicle": {
          "id": "e2b6f9c4-7d31-4a8e-9c52-8f1d0a6b3e97",
          "registration": "SL73 KPE"
        },
        "device": {
          "id": "b84e2c6a-91f3-4d70-a5c8-3e6f0d9b2741",
          "hardwareId": "009a2c45d2"
        }
      }
    ]
  }
}

media.request.completed

spillard.media.request.completed.v1

Retries
24 h

A media request finished with outcome completed, failed or no_data, whether you requested it (trigger: "manual") or it was recorded for an event (trigger: "event"). links.media points to /v1/media-requests/{mediaRequestId}/media; a failed outcome can later be corrected to completed.

Example bodymedia.request.completed.v1.json
media.request.completed.v1.json
{
  "specversion": "1.0",
  "id": "3c1f9e7d-8a4b-5f26-b0d9-e2c6a4f8b175",
  "type": "spillard.media.request.completed.v1",
  "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
  "subject": "mediaRequests/9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
  "time": "2026-09-29T08:07:48.000Z",
  "datacontenttype": "application/json",
  "data": {
    "mediaRequestId": "9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
    "trigger": "manual",
    "externalId": "INS-CLM-2026-004417",
    "outcome": "completed",
    "requestedAt": "2026-09-29T08:05:11.000Z",
    "completedAt": "2026-09-29T08:07:48.000Z",
    "request": {
      "startAt": "2026-09-29T07:41:02.000Z",
      "durationSeconds": 30,
      "channels": [
        1,
        2
      ],
      "overlay": true
    },
    "eventId": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
    "organization": {
      "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
    },
    "fleet": {
      "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
    },
    "vehicle": {
      "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
      "registration": "SL24 WHK"
    },
    "driver": {
      "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
    },
    "device": {
      "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
      "hardwareId": "009a2c41f7"
    },
    "links": {
      "media": "/v1/media-requests/9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683/media",
      "event": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53"
    }
  }
}

Type catalog: GET /v1/webhook-event-types Example bodies: GET /v1/webhooks/message-schemas Endpoints, tests, deliveries and replay

One error shape, one page per status

Every error is an RFC 9457 application/problem+json body. Branch on errorCode, never on title or detail. retryable tells you whether the same request may succeed later, and type points to the page for the status, https://connect.spillard.live/errors/{status}.

403 application/problem+json
{
  "type": "https://connect.spillard.live/errors/403",
  "title": "Forbidden",
  "status": 403,
  "detail": "This endpoint requires scope 'vehicles.read'.",
  "instance": "/v1/vehicles",
  "errorCode": "auth.scope_missing",
  "requestId": null,
  "traceId": "00-99187c3c2f335967c4f2186f90d45067-fd5023be05671f54-00",
  "retryable": false
}

Error codes. The complete, versioned errorCode registry is the top-level x-error-codes list of /openapi/v1.json; each entry is {code, status, description}. Honor Retry-After when it is present.

Limits and service status

Access tokens
Valid for expires_in seconds from Spillard Identity. Request a new token when it runs out.
Request rate
Limited per API client. Above the limit the API answers status 429 with errorCode rate.limit_exceeded; wait for Retry-After before you try again.
Webhook answers
Answer 2xx within 10 s. A request carries at most 256 KB.
Webhook retries
24 h with backoff, telemetry.history 72 h, then dead-lettered; telemetry.live 60 s, then dropped. Failed deliveries can be replayed.
Service status
GET /v1/status accepts any valid token and returns the status, build version, environment and server time. Use it for uptime checks.

Generate a client in your language

The OpenAPI 3.1 document describes every operation, scope, error and webhook body, so a generator gives you a fully typed client in seconds.

  • KiotaC#, TypeScript, Python, Go, Java, PHP and more
  • openapi-generatorClients for many languages
  • NSwagC# and TypeScript
  • Postman, InsomniaImport the document URL
Example with Kiota
kiota generate -l CSharp -d https://connect.spillard.live/openapi/v1.json \
  -c SpillardConnectClient -n Spillard.Connect.Client -o ./SpillardConnectClient
https://connect.spillard.live/openapi/v1.json OpenAPI document API reference