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):
- Provision a new key alongside the existing one.
- Deploy the new key to the integration; verify traffic is succeeding with the new key.
- 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 type | Purpose |
|---|
callback | Where ION redirects users with the authorization code after they sign in |
origin | Browser origins allowed to make authenticated requests |
logout | Where 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:
- Redirect the user to the ION authorization endpoint. Include
client_id, redirect_uri, response_type=code, scope, and state.
- User authenticates with their ION credentials (or SSO).
- ION redirects to your
redirect_uri with an authorization code and the state you sent.
- Exchange the code at the token endpoint for an
access_token (and optionally a refresh_token).
- 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:
| Scope | Allows |
|---|
openid | Receive an ID token alongside the access token |
profile | Read the user’s profile |
email | Read the user’s email |
offline_access | Receive 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:
| Message | Cause | Fix |
|---|
Please provide proper credentials | No Authorization header on the request | Add Authorization: Bearer <token> |
Unable to parse authentication token | Malformed 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 key | Token was signed by a key ION doesn’t recognize | Token came from the wrong auth provider — check you’re hitting the right client_id and audience |
Token is expired | The access token’s exp claim has passed | Refresh the token (OAuth) or re-issue (M2M) |
Unable to validate authentication token | Generic verification failure | Re-issue the token; if it persists, check token audience/issuer match your environment |
Token organization does not match user's organization | The token was issued for org A but the user belongs to org B | Re-authenticate the user against their actual organization |
User <email> is deactivated | The user has been deactivated in ION | Reactivate the user or use a different account |
Sorry, this organization is blocked from accessing ION | Org is in a blocked state | Contact your CSM |
Login domain '<domain>' is not valid for this organization | The user’s email domain isn’t whitelisted on the org | Add 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.
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.