Developers

Integrate once. Two calls in the hot path.

Send events as they happen, and ask a question when you need the answer. Plain HTTP and JSON, an OpenAPI specification for every API, and errors your code can branch on.

  1. A call arrivesyour API
  2. Send the eventPOST /v1/events
  3. Ask…:evaluate
  4. Respondanswer in hand
# 1. Something happened: send the event
curl -X POST "$P/events" \
  -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' \
  -d '{"id":"call_8812","type":"api_call",
       "ts":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",
       "attrs":{"account_id":"acme","key_id":"key_42"}}'

# 2. Before you serve the next call: ask
curl -X POST \
  "$P/entities/account/acme/profiles/usage_check:evaluate" \
  -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' \
  -d '{"context":{"api_key":"key_42"}}'
In the API, a question is called a profile and a condition a reason.
Quickstart

From nothing to a pushed result in four steps

A small example: flag a customer with 3 or more failed logins in 15 minutes, and tell your systems when it happens.

  1. Define

    Say what to keep and what to ask, in the workspace with a form or its JSON editor, or import definitions kept in your repository. Self-hosted, the admin API takes the same JSON from your pipelines.

  2. Send an event

    Post each event as it happens, one at a time or up to 500 in a batch. Each one counts once, even when you retry.

  3. Ask a question

    Ask about any customer inside your own request. The answer lists the values, the conditions that hold and anything it could not compute.

  4. Receive a webhook

    Add your https endpoint in the workspace. Results arrive as signed deliveries, retried until your side answers.

Self-hosted: POST /v1/admin/{kind}. Hosted: paste each body into the JSON editor
# $ADMIN: the admin API. $ADMIN_KEY: a key with admin.write
# Where the customer's id is in the event
curl -X POST "$ADMIN/event-types" \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name":"login_failed",
       "spec":{"entities":{"customer":"attrs.customer_id"}}}'

# What to keep: failed logins per customer, in 1-minute buckets
curl -X POST "$ADMIN/states" \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name":"failed_logins",
       "spec":{"on":"login_failed","key":["customer"],
               "op":"count","resolution":"1m","retention":"24h"}}'

# What to ask, and when to ask it by itself
curl -X POST "$ADMIN/profiles" \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name":"login_check",
       "spec":{"entity":"customer",
               "features":{"failedLogins15m":{"state":"failed_logins",
                                              "window":{"rolling":"15m"}}},
               "reasons":[{"name":"too_many_failures",
                           "when":"failedLogins15m >= 3"}],
               "triggers":[{"name":"on_failure","on":"login_failed",
                            "outputs":["stream"]}]}}'
Requests and responses as in the docs, with a few fields left out for reading.
The full quickstart, in the docs
Authentication

Credentials that fit how you run

Give each service its own credential with only the scopes it needs. Use a key where nothing else works, or short-lived tokens where you can, so no long-lived secret travels on the wire.

  • Scoped API keys: shown once, stored hashed, expiring after 90 days by default, revoked within seconds, recognisable by secret scanners
  • Machine clients: a key pair whose private half never leaves your service, signed in for short-lived tokens (RFC 7523)
  • Workload identity: a Kubernetes, CI or cloud token exchanged for a 5-minute access token (RFC 8693), with no secret to manage at all
  • Self-hosted, tokens from your own OAuth 2.0 or OpenID Connect provider, such as Keycloak, Auth0 or Microsoft Entra ID
  • Least privilege: a service that only sends events cannot read answers or change configuration
Authentication, in the docs
Ways to authenticate
  • API key

    Services that cannot do OAuth

    aek_<id>_<secret>
  • Machine client

    No long-lived secret on the wire

    key pair → short-lived token
  • Workload identity

    Kubernetes, CI and cloud workloads

    platform token → 5 min token
  • Your OAuth providerSelf-hosted

    Tokens from the provider you run

    JWT · agg:ingest
A key for a producerscopes: ingest
  • POST /v1/events200 accepted
  • POST …/login_check:evaluate403 insufficient_scope
  • GET /v1/results403 insufficient_scope

A key or token can grant only scopes it holds itself.

Every request carries one bearer credential; the workspace comes from the credential, never from the request.

Technical overview

What every call can rely on

The rules are the same across the APIs, so the edge cases are handled once in your client, not per endpoint.

  • Idempotent events

    Each event id is remembered for at least 24 hours. Retry after a timeout and the event is answered as a duplicate, never counted twice.

    {"status": "duplicate"}
  • Partial batches

    A batch is never all or nothing. Good events apply; each bad one comes back with a stable reason you can fix and resend.

    "accepted": 498, "rejected": 2
  • Async ingest, with backpressure

    For high volume, queue events and get 202 at once. When the engine falls behind it answers 503 with Retry-After instead of losing data.

    POST /v1/events?mode=async → 202
  • ETags

    Every change to a definition or webhook names the version it read, so two changes can never overwrite each other.

    If-Match → 412 precondition_failed
  • Problem Details

    Every error is an RFC 9457 document with a stable code to branch on and a request id that matches the server's logs.

    "code": "rate_limited"
  • Bounded answers

    Each question has a time budget: 20 ms by default, from 1 ms to 1 s. What is not ready in time is listed, not waited for.

    "skipped": [{"reason": "deadline_exceeded"}]
Standards

Built on what you already run

Open standards wherever one exists, so Aggrelis connects to your tools and your libraries already know how to talk to it.

  • OpenAPI

    A specification per API, to read or generate clients from.

  • OAuth 2.0 and OpenID Connect

    Short-lived tokens for machine clients and workloads, with JWT profile grants and token exchange.

  • Standard Webhooks

    Signed deliveries that any Standard Webhooks library verifies.

  • RFC 9457

    Problem Details for every error, with stable codes.

  • CEL

    The Common Expression Language, for conditions, filters and trigger gates.

  • Prometheus

    Self-hosted: metrics, a dashboard and alert rules with runbooks.

  • OpenTelemetry

    Self-hosted: traces and logs to your own collector.

Limits

The bounds you will meet

What a client meets in practice. Most are fixed by the engine; request rates and a few counts follow your plan.

Events per batch500A batch is one request against the rate limit
Request body1 MiB for one event, 5 MiB for a batchAttributes up to 32 KiB per event
Entities per event16One event can update the customer's, the card's and the device's numbers at once
Event timeUp to 35 days late, 5 minutes aheadEvents count in the minute they happened
Retries recognisedAt least 24 hoursThe same event id is answered as a duplicate
Time budget per question1 ms to 1 s, 20 ms by default
Result streamUp to 500 results per page, long-polling up to 20 sKeeps about 100,000 results for up to 7 days
WebhooksUp to 20 per workspaceYour plan sets how many; https and public addresses only
API keysExpire after 90 days by default
Request ratesBasic, Standard or Premium, by planEach API has its own rate and burst. Over it, you get 429 with Retry-After

Monthly event and question volumes and the number of keys and webhooks are set per plan. Compare plans

Start building

Read the docs, then send your first event

Concepts, guides and every endpoint, with examples taken from the running engine.

Ready for a workspace of your own? Request access