Skip to main content

sinio — Python

Official client for the sinio.ai ingestion API. No runtime dependencies (stdlib urllib), Python 3.10+, fully typed (mypy --strict clean).

pip install sinio

Quick start

from sinio import SinioClient, Event, EventRoles

sinio = SinioClient(
    api_key=os.environ["SINIO_API_KEY"],
    base_url="https://api.sinio.ai",
)

result = sinio.send_events([
    Event(
        source_event_id="evt_88213",
        type="badge_in",
        occurred_at="2026-08-28T07:58:12+02:00",
        roles=EventRoles(device="BORNE-A12", subject="P-4582", location="SITE-A.HALL"),
        payload={"badge": "04A2B9C1", "reader_firmware": "2.4.1"},
    ),
])

# The four roles (device, subject, location, actor) are read from `roles` only;
# any other key is refused. `payload` is free and optional ({} by default).

print(result.accepted, result.duplicates, result.rejections)

Plain dicts work too, and occurred_at accepts a timezone-aware datetime:

sinio.send_events([
    {"source_event_id": "evt_1", "type": "badge_in",
     "occurred_at": datetime.now(timezone.utc), "roles": {"subject": "04A2B9C1"}},
])

What the client does for you

  • Chunking. Pass 50 000 events; the SDK splits them into calls of 500 (the API limit) and merges the counters and rejections into one result.
  • Retries. 429, 5xx and dropped connections are retried with exponential backoff and full jitter, honouring Retry-After. This is safe because a resend with the same source_event_id and the same content is counted in duplicates, never stored twice.
  • Local validation. What the API would refuse raises SinioValidationError before any call, naming the field and its index: a kind, relation or mode outside its vocabulary, a missing or empty required field, an unknown role, a payload that is not an object, a naive datetime or a timestamp without an offset, a valid_to not after valid_from.
  • Typed errors. SinioAuthError (401/403, do not replay), SinioRequestError (400/404/413, fix the request), SinioRateLimitError (429, carries retry_after_seconds), SinioServerError (5xx, replayable), SinioConnectionError, SinioValidationError.

Partial acceptance

A 200 means the batch was received, not derived — derivation is asynchronous. Within a batch, valid events are accepted even when others are rejected, so always read rejections:

for rejection in result.rejections:
    log.warning("%s %s %s", rejection.source_event_id, rejection.code, rejection.message)

duplicates and revisions are successes, not failures.

Every closed vocabulary is exported, as a Literal type and the tuple of its values: EntityKind / ENTITY_KINDS, LocationLevel / LOCATION_LEVELS, IngestionMode / INGESTION_MODES, AssignmentRelation / ASSIGNMENT_RELATIONS, EventRole / EVENT_ROLES, RejectionCode / REJECTION_CODES. A rejection's code stays a str, since the API may add codes; known_code narrows it, or is None for a code newer than the SDK:

from sinio import ENTITY_KINDS

assert "device" in ENTITY_KINDS
if rejection.known_code == "unknown_location":
    sinio.upsert_locations(missing_locations)

Reference data

from sinio import Assignment, Entity, Location

sinio.upsert_locations([
    Location(external_id="SITE-A", kind="site", name="Site A"),
    Location(external_id="SITE-A.HALL", kind="zone", name="Hall", parent="SITE-A"),
])

sinio.upsert_entities([
    Entity(external_id="BORNE-A12", kind="device", subkind="nfc_kiosk",
           location="SITE-A.HALL", attributes={"firmware": "2.4.1"}),
])

sinio.create_assignments([
    Assignment(credential="04A2B9C1", subject="P-4582", relation="assigned_to",
               valid_from="2026-01-01T00:00:00+01:00"),
])

The reference is never a condition of acceptance: an event pointing at an unknown device is still accepted. Upserting is how you enrich it, not how you gate it.

Backfill

sinio.backfill_events(history)

mode="backfill" persists history without raising alerts; once it has arrived, the project's processes replay it in a canonical order. Live catch-up after an outage should stay on send_events — the declared mode decides alerting, not the age of the events.

Webhook key

For systems that can only be pointed at a URL and cannot set a header:

sinio = SinioClient(webhook_key=os.environ["SINIO_WEBHOOK_KEY"])
sinio.send_events(events)  # POST /v1/webhooks/<key>

In this mode only event ingestion is available; the reference endpoints require an API key.

Bring your own HTTP client

sender replaces the stdlib transport — useful in a project that already depends on requests or httpx, or to stub the network in tests:

from sinio import HttpResponse

def httpx_sender(url, payload, headers, timeout):
    response = httpx.post(url, content=payload, headers=headers, timeout=timeout)
    return HttpResponse(response.status_code, dict(response.headers), response.text)

sinio = SinioClient(api_key, sender=httpx_sender)

Development

python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest
.venv/bin/python -m mypy
.venv/bin/python -m ruff check .

Metadata

Release files for sinio 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sinio 0.1.0
File Size Uploaded Provenance Actions
sinio-0.1.0.tar.gz 18.2 kB With provenance Details for sinio-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for sinio 0.1.0
File Interpreter ABI Platform Actions
sinio-0.1.0-py3-none-any.whl Python 3 none any Details for sinio-0.1.0-py3-none-any.whl

Total release size: 35.3 kB

Release files / sinio-0.1.0.tar.gz

Download URL sinio-0.1.0.tar.gz
Size 18.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f692f0ed0cc5fccb7c86d11d25baec176121eab82eb539e541458994eda6af20
BLAKE2b-256 checksum
How to use checksums
7568b7abf8fd7796b016d2f2d9906e710e0d178bb367000ff6a4686d038635fd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / sinio-0.1.0-py3-none-any.whl

Download URL sinio-0.1.0-py3-none-any.whl
Size 17.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ecfa97456661764b986c54270ecf2a935a39c6def26bd1b9d0f74b90369b004c
BLAKE2b-256 checksum
How to use checksums
4bb83c8d8a844b75f378579d6c3ecbacf9e6be54cd0260246b9273f6591f58ad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Security for sinio 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for sinio-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page