Aller au contenu
AtheronDEVELOPERS
GraphQL

Bac à sable GraphQL

La même API qu’en REST, une seule requête pour ce qu’il vous faut. Utilisez une clé de test : elle reste dans cet onglet et ne va nulle part ailleurs que vers notre API.

Où la trouver

  • Requêtes : POST https://developers.aton-network.org/api/graphql
  • Abonnements : wss://developers.aton-network.org/api/graphql/ws (graphql-transport-ws)
  • Schéma, version 2026-10-15 : schema.graphql
  • Flux d’événements et webhooks : Description AsyncAPI

Chaque champ est aussi un appel REST, avec le même droit et le même quota. Une requête prend son coût sur votre quota api, donc demander deux choses à la fois coûte deux.

Limites

Opérations par requête1 (pas de regroupement)
Coût par requête25 (chaque champ qui lit notre base de données coûte 1, l’introspection 5)
Profondeur5 niveaux (15 dans l’introspection)
Champs sélectionnés300, alias et fragments comptés là où ils apparaissent
Taille d’une requête16 Ko, 2 000 jetons, variables 4 Ko
Abonnements5 par connexion WebSocket ; 10 flux d’événements par application et environnement en Server-Sent Events, et 10 en WebSocket

Erreurs

Chaque erreur porte extensions.code, le même code que REST répond quand il y en a un.

CodeStatutPourquoiQuoi faire
invalid_key401Aucune clé d’API n’a été envoyée, ou ce n’est pas une des nôtres, elle a expiré ou a été révoquée.Envoyez une clé valide en Authorization : Bearer ath_test_... ou ath_live_... (en WebSocket, dans le payload de connection_init).
missing_rightà côté des donnéesLa clé ne porte pas le droit dont un champ a besoin. Ce champ vaut null ; le reste de la réponse est là.Créez une clé avec ce droit (app:read, usage:read, events:read) dans le portail, ou ne demandez que ce que cette clé peut lire.
not_foundà côté des donnéesL’application de la clé a été supprimée, ou l’identifiant after des événements n’est pas de cette application ou date de plus de 30 jours.Utilisez une clé d’une application qui existe encore ; reprenez à partir d’un identifiant reçu dans les 30 derniers jours, ou commencez sans after.
rate_limited429Quota api dépassé pour cette minute ou cette journée. Une requête prend son coût sur le quota ; trop de flux d’événements ouverts donnent aussi cette réponse.Attendez les secondes indiquées dans Retry-After. Demandez moins de champs qui lisent la base de données, ou moins d’alias.
invalid_request400La requête n’a pas pu s’exécuter : pas du JSON, un lot (un tableau), une erreur de syntaxe, un champ ou un argument absent du schéma, plus d’une opération sans operationName, ou un abonnement envoyé en HTTP.Lisez le message ; le schéma est à /graphql/schema.graphql. Envoyez une opération par requête ; les abonnements passent par le WebSocket.
query_too_complex400Une limite est dépassée : profondeur, champs sélectionnés (alias et fragments comptés là où ils apparaissent), profondeur d’introspection ou coût.Divisez la requête, ou demandez moins à la fois. Les limites sont sur cette page.
persisted_query_not_found200Le hachage d’une requête persistée ne nous est pas encore connu (message PersistedQueryNotFound).Renvoyez la même requête avec la requête en plus du hachage ; elle est alors gardée, et le hachage seul suffit ensuite.
server_errorà côté des donnéesUn problème est survenu de notre côté. Rien n’en est montré.Réessayez dans un instant. Si cela continue, dites-le-nous sur la page d’assistance avec l’heure.

Schéma

"""
Atheron developer API, GraphQL, version 2026-10-15. A field is never removed or changed in a way that breaks a client without first being marked @deprecated, with the date and what replaces it, at least six months before.
"""
schema {
  query: Query
  subscription: Subscription
}

"""The developer API, version 2026-10-15. Every field is a REST call too."""
type Query {
  """
  Your application as registered, for this key's environment. Needs app:read. Same as GET /api/v1/app.
  """
  app: App

  """
  Calls, errors and latency per day and bucket, for this key's environment. Needs usage:read. Same as GET /api/v1/usage.
  """
  usage(
    """1 to 90; anything else is brought within."""
    days: Int = 7
  ): Usage
}

"""
Your application as registered, for the environment the API key belongs to. The same fields as GET /api/v1/app.
"""
type App {
  """The name people see on the consent screen."""
  name: String!

  """Where the app is: development, review or live."""
  state: String!

  """confidential (a server that keeps a secret) or public (PKCE only)."""
  kind: String!

  """The quota tier: free, growth, scale or partner."""
  quota: String!

  """The environment of this key."""
  env: Environment!

  """The OAuth client ID for this environment."""
  clientId: String!

  """The redirect addresses registered for this environment."""
  redirectUris: [String!]!

  """The scopes this environment's client may ask for."""
  scopes: [String!]!

  """The scopes review approved for live."""
  approvedScopes: [String!]!

  """Your privacy policy, shown on the consent screen."""
  privacyUrl: String

  """Your homepage, shown on the consent screen."""
  homepageUrl: String

  """Client secrets and certificates in force."""
  credentials: Int!

  """Named testers on the test client."""
  testers: Int!

  """Webhooks for this environment."""
  webhooks: Int!
}

"""Which of the app's two clients a key belongs to."""
enum Environment {
  """The test client: you, your team and up to 25 named testers."""
  TEST

  """The live client, after review."""
  LIVE
}

"""
Calls, errors and latency per day and bucket. The same answer as GET /api/v1/usage.
"""
type Usage {
  """The environment of this key."""
  env: Environment!

  """How many days the answer covers, 1 to 90."""
  days: Int!

  """One row per day and bucket, oldest first."""
  usage: [UsageDay!]!
}

"""One day and bucket of calls. Days are UTC."""
type UsageDay {
  """The day, YYYY-MM-DD, UTC."""
  day: String!

  """token, userinfo, userinfo:sensitive, api, sandbox or sign-in."""
  bucket: String!

  """Requests counted. A GraphQL request counts as its cost."""
  calls: Int!

  """Of those, how many failed."""
  errors: Int!

  """Mean time taken, in milliseconds."""
  msAvg: Int

  """Longest time taken, in milliseconds."""
  msMax: Int!
}

"""
Events as they happen, over WebSocket (graphql-transport-ws). The same events as GET /api/v1/events and your webhooks.
"""
type Subscription {
  """
  Events for this key's app and environment. Needs events:read. Same as GET /api/v1/events.
  """
  events(
    """Only these kinds. All kinds when left out."""
    types: [EventType!]

    """Resume after this event id (kept 30 days)."""
    after: ID
  ): Event!
}

"""
One event, with the same five fields a webhook delivery's body carries.
"""
type Event {
  """The event's id. Send it back as after to resume."""
  id: ID!

  """The kind of event."""
  type: EventType!

  """When it was raised, ISO 8601."""
  created: String!

  """The client it concerns."""
  clientId: String!

  """What happened."""
  data: EventData!
}

"""The kinds of event, as webhooks name them."""
enum EventType {
  USER_SIGNED_IN
  CONSENT_REVOKED
  MEMBERSHIP_CHANGED
  PING
}

"""
What happened. Which fields are present depends on the type, exactly as in a webhook's data.
"""
type EventData {
  """The person, as the sub claim your app was given. Absent for ping."""
  sub: ID

  """user.signed_in: the scopes of this sign-in."""
  scopes: [String!]

  """When it happened, ISO 8601."""
  at: String
}