Authentication

Every call to the sinio.ai API is authenticated with a machine key. A key is not just a password: it carries identity. It resolves the organization, the project, the source, and the scopes the caller is allowed to use — which is why so few fields need to be repeated in each request.

Bearer token

The normal surface. Send the key in the Authorization header:

Authenticated request

curl https://api.sinio.ai/v1/events \
  -H "Authorization: Bearer $SINIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d @batch.json

A missing or malformed header, and a key that is unknown or revoked, both return 401. There is nothing to retry on a 401: the key will not become valid on its own.

What a key resolves

  • Name
    organization
    Type
    always
    Description

    The organization the key belongs to. Everything the key writes lands under it, and it is what your usage is billed against.

  • Name
    project
    Type
    always
    Description

    The project the key is bound to. Every key the console creates belongs to exactly one project, so no request needs to name it.

  • Name
    source
    Type
    always
    Description

    The source the key feeds — see Naming the source.

  • Name
    scopes
    Type
    always
    Description

    What the key is allowed to do: ingest for an API key, ingest:webhook for a webhook token — see Scopes.

The key already names the project, so a request body never carries one:

Request body

{
  "events": [
    {
      "source_event_id": "evt_88213",
      "type": "badge_in",
      "occurred_at": "2026-08-28T07:58:12+02:00",
      "payload": { "badge": "04A2B9C1" }
    }
  ]
}

Naming the source

A source is one of your tools: your access-control system, your maintenance software, your CRM. Sources are created in the project (during onboarding or in the project settings), and every key belongs to exactly one. Whatever channel uses the key — the API, an SDK or a webhook URL — its data lands in that source, with no header to send.

It matters more than a label. Identifiers are scoped by source: badge 2 in your access-control system and asset 2 in your maintenance tool are two different things, and so are two event types that share a raw code (closed, say). sinio.ai keeps them apart.

Sources are never created on the fly, and archiving a source in the console revokes its keys: from then on they return 401, like any revoked key. To feed a new source, create it in the project settings and create a key for it.

Webhook keys

Some systems can only be pointed at a URL — no custom headers, no auth configuration. For those, a source issues a webhook token, which authenticates in the path:

Token in the URL

curl https://api.sinio.ai/v1/webhooks/$SINIO_WEBHOOK_KEY \
  -H "Content-Type: application/json" \
  -d @batch.json

A webhook token is not an API key: it opens this URL and nothing else, and an API key does not work in a URL. Like a project key, it belongs to one source, so no header is needed. The body is exactly the one POST /v1/events expects, mode included: this is not a generic receiver that normalises an arbitrary third-party payload.

Test mode

A key created while connecting a tool from the console starts in test mode, for one hour at most. During that time, what it sends is checked exactly like live traffic — same validation, same rejections, same counters — then kept aside for you to review: nothing is derived from it, nothing is billed, and every response carries "test_mode": true.

The test ends when you decide in the console: keep the events, and they are ingested for real, or drop them. Once the test ends — or the hour runs out — the key ingests live traffic like any other, and test_mode is false.

Scopes

  • Name
    ingest
    Type
    scope
    Description

    Push events, and upsert entities, locations and assignments, with the key in the Authorization header. This is what an API key is created with.

  • Name
    ingest:webhook
    Type
    scope
    Description

    Push events through a webhook URL, and nothing else. This is what a webhook token is created with: it is refused as a Bearer key everywhere.

A call missing the required scope returns 403 with the scope it wanted. Like a 401, it is not retryable: fix the key, not the request.

Keeping keys safe

  • Keys are server-side credentials. Never ship one in a browser, a mobile app, or anything a customer can read.
  • Store them the way you store database passwords — a secret manager or an environment variable, not your repository.
  • Use one key per integration. Revoking a leaked key should not take down four systems at once.
  • Rotate by creating the new key, deploying it, and revoking the old one — both are valid in between, so there is no gap.

Was this page helpful?