i9Networki9developers
AUTHENTICATION

Calling the i9Network APIs

Log in for an access token, then call any endpoint through the gateway with a Bearer token and your tenant id. Two requests get you your first authenticated response.

Base URLs

Every request goes through the API gateway. Read each product’s spec for paths and shapes, but always send requests to the gateway host — never a service’s internal host.

Production gateway

https://api.i9network.com/api/v1

Sandbox · coming soon

Not yet available — use production for now.

The full URL for any endpoint is {gateway} + the endpoint path, e.g. https://api.i9network.com/api/v1/subscriber/subscribers.

Log in

Authenticate once with POST /auth/login. Take the accessToken from the response body and send it as a Bearer token on every later call.

Log in
# Public route: no X-Tenant-ID, no bearer needed to call it
curl -s -X POST "https://api.i9network.com/api/v1/auth/login" \
  -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"••••••••"}'
200 — login response
{
  "success": true,
  "message": "Login successful",
  "data": {
    "accessToken": "<JWT>",
    "tokenType": "Bearer",
    "expiresAt": "2026-01-01T00:15:00Z",
    "refreshToken": "<JWT>"
  }
}

Login also sets HttpOnly cookies for browser sessions; scripts should use the Bearer header. If the response returns requiresMfa, complete POST /auth/login/mfa/verify. If it returns businessChoices, re-POST /auth/login with a businessId.

Required headers

HeaderValueRequired
AuthorizationBearer <accessToken>

The token from the login response body.

Yes
X-Tenant-IDa tenant id, or system

Present on every authenticated call. Send it exactly once — a duplicate is rejected with 403. Omit it on /auth/login and /auth/refresh.

Yes
Content-Typeapplication/json

Required on POST / PUT / PATCH.

On writes

Use systemfor platform-wide reads; use a specific tenant id to scope a request to one tenant. The header satisfies the gateway’s presence check and selects scope — real access is derived from your token.

API keys

COMING SOON

Today every call uses a short-lived bearer token plus X-Tenant-ID. For server-to-server integrations we’re adding API keys: a single long-lived credential that embeds your tenant and scope, so you won’t send a separate tenant header.

  • Sent as X-API-Key: i9_… on every request.
  • Tenant and scope are bound to the key when it’s issued — no X-Tenant-ID needed.
  • Least-privilege scopes, independently revocable and rotatable.

The API Playground already has an API key mode you can preview. Provisioning — creating and rotating keys — ships with this feature; until then, use a bearer token.

Response envelope

Every response is wrapped in a consistent envelope. The payload you want is always under .data.

Envelope
{
  "success": true,
  "message": "OK",
  "data": { "items": [ /* your payload lives here */ ] }
}

Token lifecycle

The access token lasts roughly 15 minutes. On a 401 mid-session, re-run /auth/login (scripts) or POST /auth/refreshwith the refresh token (browsers). Short-lived scripts don’t need to refresh proactively — just log in again.

Quickstart

Log in, then call an endpoint — two requests:

curl
GW=https://api.i9network.com/api/v1

# 1. Log in → grab the accessToken from .data
TOKEN=$(curl -s -X POST "$GW/auth/login" \
  -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"••••••••"}' \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["accessToken"])')

# 2. Call any endpoint — send X-Tenant-ID exactly once
curl -s "$GW/subscriber/subscribers?limit=5" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Tenant-ID: system"

Errors & gotchas

  • Always go through the gateway host above — calling a service’s internal host bypasses routing and TLS.
  • A missing X-Tenant-ID fails at the gateway (400/401) before your endpoint ever runs.
  • A duplicate X-Tenant-ID header returns 403.
  • Sending X-Tenant-ID on /auth/login is rejected — login happens before a tenant is selected.
  • A route can 404 at the gateway even if it exists in the spec, until it is published.
  • The payload is always under .data, never at the top level of the response.

Status conventions

POST → 201 CreatedPUT → 200 OKDELETE → 204 No Content

Next steps