API Overview

j17's API is HTTP-first and minimal. POST events, GET aggregates. That's the core. Everything else -- batching, admin, implications -- is built on these primitives.

Base URLs

Production:  https://{instance}.j17.dev
Staging:     https://{instance}-staging.j17.dev
Test:        https://{instance}-test.j17.dev

All requests require HTTPS.

Authentication

Two methods depending on what you're doing:

Method Header Use for
API Key Authorization: Bearer {key} Instance data (events, aggregates)
JWT Authorization: Bearer {token} Admin operations, dashboards

See Authentication for details.

Core operations

Write an event

POST /{aggregate_type}/{aggregate_id}/{event_type}
curl -X POST https://myapp.j17.dev/order/abc123/was_placed \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": { "items": [...], "total": 59.99 },
    "metadata": {
      "actor": { "type": "user", "id": "550e8400-e29b-41d4-a716-446655440000" }
    }
  }'

Returns the stream ID of the written event.

Read an aggregate

GET /{aggregate_type}/{aggregate_id}
curl https://myapp.j17.dev/order/abc123 \
  -H "Authorization: Bearer $API_KEY"

Returns current state derived from all events.

Batch operations

POST /{aggregate_type}/{aggregate_id}

Atomic multi-event writes to a single aggregate. See Batch operations.

Request format

Event POST body

{
  "data": { ... },           // Required. Validated against event schema
  "metadata": {              // Required
    "actor": {               // Required. Who performed the action
      "type": "user",        // Must be in spec's agent_types
      "id": "550e8400-e29b-41d4-a716-446655440000"
    },
    "target": {              // Optional. What was affected
      "type": "order",
      "id": "target-id"
    },
    "previous_length": 5     // Optional. For OCC
  }
}

Idempotency is handled via the X-Idempotency-Key request header, not in metadata. See Writing events for details.

Content-Type

Always application/json. j17 rejects other content types with 400.

Response format

Success (201 Created) -- single event

{
  "ok": true,
  "stream_id": "1234567890123-0",
  "implied_count": 2
}
Field Type Description
ok boolean Always true on success
stream_id string Redis stream ID of the written event
implied_count integer Number of implied events (omitted if 0)

Success (201 Created) -- batch

{
  "ok": true,
  "stream_ids": ["1234567890123-0", "1234567890123-1"],
  "count": 2,
  "implied_count": 3
}

Error (4xx/5xx)

{
  "ok": false,
  "error": "Event data failed schema validation",
  "path": "data.email"
}

Common error codes:

HTTP Meaning
400 Validation error (bad schema, missing fields)
401 Invalid or missing API key
403 Valid auth, but not allowed (wrong environment, read key on write)
404 Aggregate type or event type not in spec
409 OCC conflict (stale previous_length)
413 Event data too large
422 Idempotency key reused with different body
429 Rate limited
500 Internal error

Query parameters

GET /{type}/{id}

Parameter Type Description
synchronous boolean Bypass cache, compute fresh (?synchronous=true)

GET /{type}/{id}/events

Parameter Type Description
start string Start from this stream ID (exclusive)
count integer Max events to return (default 100)

Rate limits

Scope Limit
Per API key 500 requests/minute
Per IP 2,000 requests/minute

Rate limit headers on every response:

X-RateLimit-Limit: 500
X-RateLimit-Remaining: 499
X-RateLimit-Scope: api-key

Exceeding limits returns 429.

Idempotency

POST events support idempotency via the X-Idempotency-Key request header:

curl -X POST https://myapp.j17.dev/order/abc123/was_placed \
  -H "X-Idempotency-Key: order-123-was_placed-20240101" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"data": {...}, "metadata": {"actor": {...}}}'

Same key + same body = cached response replayed (with X-Idempotency-Replayed: true header).

Same key + different body = 422 error.

Keys expire after 24 hours.

CORS

j17 supports cross-origin requests from browser clients:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type

Webhooks (Listeners)

j17 delivers events to your HTTP endpoints via listeners defined in your spec. When a matching event is written, j17 POSTs a signed JSON payload to your URL.

Delivery is guaranteed with retry: failed deliveries are retried with exponential backoff (5s, 25s, 125s) up to 3 attempts. Payloads are signed with HMAC-SHA256 using your listener secret, delivered in the X-J17-Signature header.

Admin API

Separate endpoints for instance management, accessible via the headnode:

POST   /api/instances/:id/spec    # Deploy spec
GET    /api/instances/:id/keys    # List API keys
POST   /api/instances/:id/keys    # Create API key

See Admin API for details.

Testing

Use staging or test environments:

# Staging
curl https://myapp-staging.j17.dev/order/abc123/was_placed ...

# Production
curl https://myapp.j17.dev/order/abc123/was_placed ...

Different API keys, isolated data. Test freely in staging.

See also

Can't find what you need? support@j17.app