Zum Inhalt springen
AtheronDEVELOPERS
GraphQL

GraphQL-Spielwiese

Dieselbe API wie REST, eine Anfrage für alles, was Sie brauchen. Nutzen Sie einen Testschlüssel: Er bleibt in diesem Tab und geht nur an unsere API.

Wo sie liegt

  • Abfragen: POST https://developers.aton-network.org/api/graphql
  • Abonnements: wss://developers.aton-network.org/api/graphql/ws (graphql-transport-ws)
  • Schema, Version 2026-10-15: schema.graphql
  • Ereignisstrom und Webhooks: AsyncAPI-Beschreibung

Jedes Feld ist auch ein REST-Aufruf, mit demselben Recht und demselben Kontingent. Eine Anfrage zieht ihre Kosten von Ihrem api-Kontingent ab, zwei Dinge auf einmal kosten also zwei.

Grenzen

Operationen pro Anfrage1 (keine Bündelung)
Kosten pro Anfrage25 (jedes Feld, das unsere Datenbank liest, kostet 1, Introspektion 5)
Tiefe5 Ebenen (15 innerhalb der Introspektion)
Ausgewählte Felder300, Aliase und Fragmente dort gezählt, wo sie vorkommen
Größe einer Anfrage16 KB, 2.000 Token, Variablen 4 KB
Abonnements5 pro Socket; 10 Ereignisströme pro App und Umgebung über Server-Sent Events und 10 über WebSockets

Fehler

Jeder Fehler hat extensions.code, denselben Code, den REST antwortet, wo es einen gibt.

CodeStatusWarumWas tun
invalid_key401Es wurde kein API-Schlüssel gesendet, oder er ist keiner von uns, abgelaufen oder widerrufen.Senden Sie einen gültigen Schlüssel als Authorization: Bearer ath_test_... oder ath_live_... (über WebSocket im Payload von connection_init).
missing_rightneben den DatenDem Schlüssel fehlt das Recht, das ein Feld braucht. Dieses Feld ist null; der Rest der Antwort ist da.Erstellen Sie im Portal einen Schlüssel mit dem Recht (app:read, usage:read, events:read), oder fragen Sie nur ab, was dieser Schlüssel lesen darf.
not_foundneben den DatenDie App des Schlüssels wurde entfernt, oder eine after-ID der Ereignisse gehört nicht zu dieser App oder ist älter als 30 Tage.Nutzen Sie einen Schlüssel einer App, die noch existiert; setzen Sie bei einer ID fort, die Sie in den letzten 30 Tagen erhalten haben, oder beginnen Sie ohne after.
rate_limited429Ihr api-Kontingent für diese Minute oder diesen Tag ist überschritten. Eine Anfrage zieht ihre Kosten vom Kontingent ab; zu viele offene Ereignisströme ergeben dieselbe Antwort.Warten Sie die Sekunden aus Retry-After ab. Fragen Sie weniger Felder ab, die die Datenbank lesen, oder weniger Aliase.
invalid_request400Die Anfrage konnte nicht laufen: kein JSON, ein Stapel (ein Array), ein Syntaxfehler, ein Feld oder Argument, das das Schema nicht hat, mehr als eine Operation ohne operationName oder ein Abonnement über HTTP.Lesen Sie die Meldung; das Schema liegt unter /graphql/schema.graphql. Senden Sie eine Operation pro Anfrage; Abonnements laufen über den WebSocket.
query_too_complex400Eine Grenze ist überschritten: Tiefe, ausgewählte Felder (Aliase und Fragmente dort gezählt, wo sie vorkommen), Introspektionstiefe oder Kosten.Teilen Sie die Anfrage auf, oder fragen Sie weniger auf einmal ab. Die Grenzen stehen auf dieser Seite.
persisted_query_not_found200Den Hash einer persistierten Abfrage kennen wir noch nicht (Meldung PersistedQueryNotFound).Senden Sie dieselbe Anfrage noch einmal mit der Abfrage und dem Hash; dann wird sie gespeichert, und danach genügt der Hash.
server_errorneben den DatenBei uns ist etwas schiefgelaufen. Details werden nicht angezeigt.Versuchen Sie es gleich noch einmal. Wenn es weiter passiert, sagen Sie es uns auf der Support-Seite mit der Uhrzeit.

Schema

"""
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
}