Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.firstresonance.io/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The ION GraphQL API supports two authentication methods:
  • API keys — long-lived credentials for machine-to-machine (M2M) integrations like ETL jobs, MES bridges, and back-end services running in your own infrastructure.
  • OAuth 2.0 — token-based flow for user-facing applications where end-users authenticate as themselves (and ION enforces their permissions).
Pick the method that matches who’s calling: a system (M2M → API key) or a user (OAuth 2.0). Every authenticated request hits the same endpoint and uses the same Authorization header format. The only difference is how you obtain the token.

API endpoint

All authenticated requests target a single GraphQL endpoint:
POST https://api.firstresonance.io/graphql
Send the access token in the Authorization header on every request:
Authorization: Bearer <token>
(Authorization: Token <token> is also accepted for backward compatibility, but Bearer is preferred.)

API keys (machine-to-machine)

Use API keys when the caller is a system, not a person. API keys are long-lived, organization-scoped, and don’t require the OAuth dance.

Creating an API key

API keys are provisioned by an org admin from the ION admin area. Each key is bound to a service user identity in your organization, so audit logs reflect which integration made each call. When ION returns the key it shows the secret value once — capture and store it securely (a secrets manager like AWS Secrets Manager, HashiCorp Vault, 1Password, or your CI/CD’s encrypted secrets store). ION cannot retrieve a key after the initial creation.

Using an API key

API keys exchange for an access token via the auth provider’s client-credentials grant; ION’s auth layer then accepts that token on the Authorization header. Most integrations use a small client library that handles the token exchange and caching automatically — see the Python Quickstart for a runnable example.

Rotating a key

Rotate an API key when:
  • The key may have been exposed (committed to a public repo, leaked in logs, an employee with access has left).
  • Your org’s security policy requires periodic rotation.
  • An integration is being decommissioned.
Rotation pattern (zero downtime):
  1. Provision a new key alongside the existing one.
  2. Deploy the new key to the integration; verify traffic is succeeding with the new key.
  3. Revoke the old key.
The old and new keys are valid simultaneously during the cutover window, so consumers don’t see auth failures.

Revoking a key

An admin can revoke any API key from the admin area. Revocation is immediate — the next request using that key returns 401 Unauthorized.

OAuth 2.0 (user-facing applications)

Use OAuth 2.0 when the caller is a user-facing application (web app, desktop tool, mobile client) and you want ION to enforce that user’s permissions, not a service account’s.

Registering an OAuth application

Before users can sign in to your application, an org admin must register the application with ION:
mutation RegisterApp {
  registerOAuthApp(name: "Acme Production Dashboard", appType: "regular_web") {
    oauthApp {
      id
      clientId
    }
  }
}
After registration, configure callback URLs, allowed origins, and logout URLs:
mutation AddCallback {
  addOAuthRedirectUri(
    oauthAppId: 42
    uri: "https://app.example.com/auth/callback"
    uriType: "callback"
  ) {
    redirectUri { id uri uriType }
  }
}
Three URI types are supported:
URI typePurpose
callbackWhere ION redirects users with the authorization code after they sign in
originBrowser origins allowed to make authenticated requests
logoutWhere ION redirects users after sign-out
Register every URL your application uses — production, staging, local development — to avoid redirect_uri mismatches.

Authorization code flow

Standard OAuth 2.0 authorization code flow:
  1. Redirect the user to the ION authorization endpoint. Include client_id, redirect_uri, response_type=code, scope, and state.
  2. User authenticates with their ION credentials (or SSO).
  3. ION redirects to your redirect_uri with an authorization code and the state you sent.
  4. Exchange the code at the token endpoint for an access_token (and optionally a refresh_token).
  5. Use the access token on subsequent API calls.
The exact authorization and token endpoint URLs depend on your org’s auth provider configuration — they’re surfaced in the OAuth app registration response. If you’re integrating against ION for the first time, ask your CSM for the endpoint values that match your environment.

Token expiration and refresh

Access tokens have a short lifetime (typically 1 hour). Two patterns to handle expiration:
  • Short-lived integrations — let the token expire; re-run the auth flow next time the user opens the app.
  • Long-lived integrations — request offline_access scope at authorization and exchange refresh tokens for new access tokens transparently.
Always re-validate tokens before relying on them — clock skew, server-side revocation, or org membership changes can invalidate a token mid-flight.

Scopes

Scopes constrain what an OAuth-issued token can do. Standard scopes include:
ScopeAllows
openidReceive an ID token alongside the access token
profileRead the user’s profile
emailRead the user’s email
offline_accessReceive a refresh token
ION applies the user’s existing role and permission grants on top of the scope — a scope cannot grant a user more access than their role allows.

Error codes

The API uses standard HTTP status codes plus a errors[].message payload:
{
  "errors": [{ "message": "Token is expired." }]
}

401 Unauthorized

The request was rejected at the authentication layer. Common causes and remediations:
MessageCauseFix
Please provide proper credentialsNo Authorization header on the requestAdd Authorization: Bearer <token>
Unable to parse authentication tokenMalformed token (truncated, wrong format)Verify the full token is being sent — common cause is an env var that lost a trailing character
Unable to find appropriate RSA keyToken was signed by a key ION doesn’t recognizeToken came from the wrong auth provider — check you’re hitting the right client_id and audience
Token is expiredThe access token’s exp claim has passedRefresh the token (OAuth) or re-issue (M2M)
Unable to validate authentication tokenGeneric verification failureRe-issue the token; if it persists, check token audience/issuer match your environment
Token organization does not match user's organizationThe token was issued for org A but the user belongs to org BRe-authenticate the user against their actual organization
User <email> is deactivatedThe user has been deactivated in IONReactivate the user or use a different account
Sorry, this organization is blocked from accessing IONOrg is in a blocked stateContact your CSM
Login domain '<domain>' is not valid for this organizationThe user’s email domain isn’t whitelisted on the orgAdd the domain to org settings or use a whitelisted email

403 Forbidden

The request authenticated but is not authorized for the requested operation. See Error codes for the full 403 cause-and-remediation table.

5xx errors

5xx responses indicate ION-side failures and are always safe to retry with exponential backoff. If a 5xx persists across multiple retries, contact support.

Rate limits

ION applies fair-use rate limits at the GraphQL endpoint. Production traffic patterns rarely hit them — if you do, the response will surface as a 429 Too Many Requests. Implement exponential backoff in your client and consider batching queries via GraphQL field selection to reduce request volume.

Headers reference

Minimum required headers on every authenticated request:
POST /graphql HTTP/1.1
Host: api.firstresonance.io
Authorization: Bearer <token>
Content-Type: application/json
For multipart file uploads (see File Upload), set Content-Type: multipart/form-data and follow the multi-step flow.