Try Apollo Studio

Schema reporting protocol reference

Add automatic schema registration to your GraphQL server

This document specifies the protocol that any GraphQL server can implement to enable automatic schema registration with the Apollo schema registry.

For reference, Apollo Server uses this protocol in its schema reporting plugin (see the source).

If you add schema reporting support to a GraphQL server library, please let us know in the Apollo community forums!

The reportSchema mutation

A server that reports its schema to Apollo does so periodically via a GraphQL API hosted at the following endpoint:

Specifically, a server executes the reportSchema mutation, which has the following shape:

mutation ReportSchemaMutation($coreSchema: String, $report: SchemaReport!) {
reportSchema(coreSchema: $coreSchema, report: $report) {
... on ReportSchemaError {

This mutation always requires a report input argument, which has the following minimum shape:

"bootID": "abc123",
"coreSchemaHash": "F908D0B24486E2B2A032ED019A657E7243BCDB7DC5D939AF69820FE7BADFB200",
"graphRef": "docs-example-graph@production"

The details of this operation and its inputs are described below.

Required information

To successfully report a schema to Apollo, your server requires the following information:

Graph API key

All requests to Apollo's schema reporting endpoint require an X-API-Key header:

X-API-Key: service:docs-example-graph:abc123

The value of this header is a graph API key. Developers obtain this API key from Apollo Studio and provide it to their server instance on startup.

We strongly recommend that your server accept the graph API key via an environment variable (e.g., APOLLO_KEY). This is because API keys are secret credentials that developers should never commit to version control.

Graph ref

Whenever a server reports its schema, it does so to a particular graph and variant in Apollo Studio. Apollo uses strings called graph refs to describe particular graph-variant combinations:


A developer can obtain a variant's graph ref from the variant's Schema page in Apollo Studio.

As with API keys, we recommend that developers provide their graph ref to your server via an environment variable (e.g., APOLLO_GRAPH_REF) that's loaded on startup.

Boot ID

Whenever an instance of your server boots up, it should generate a UUID to identify itself among other instances of the same server. You provide this ID with each reportSchema mutation you execute, so that Apollo can distinguish between requests from different instances.

An instance's boot ID should not persist across restarts.

Schema hash

Every reportSchema mutation requires the SHA-256 hash of the schema string you're reporting to Apollo, as a hexadecimal string:


You should always normalize your schema before generating this hash, as explained below.

Protocol sequence

The following sections illustrate the communication sequence between a GraphQL server (hereafter referred to as the edge server) and the schema reporting endpoint (Apollo) after the server starts up. Each of these sections illustrates the same concepts in different formats.


Edge ServerApolloTopreportSchema(coreSchema=null, report={...})ReportSchemaResponse(withCoreSchema = false, inSeconds = 57s)Wait 57 secondsGo to TopReportSchemaResponse(withCoreSchema = true, inSeconds = 0s)Wait 0 secondsreportSchema(coreSchema="…", report={...} executableSchema = '…')Persist schemaReportSchemaResponse(withCoreSchema = false, inSeconds = 62s)Wait 62 secondsGo to TopReportSchemaError(code, message)Stop until error is fixedWait 20 secondsGo to Topalt[The reported schema hash is already registered][The reported schema hash is NOT already registered][reportSchema request is malformed or otherwise invalid][reportSchema request failed with non-2xx HTTP response]Edge ServerApollo

Step-by-step description

  1. On startup, the edge server executes the reportSchema mutation, providing a SchemaReport object with the server's details as input.

    • See the SchemaReport object's fields, and its minimum shape.
    • In this initial call, set the coreSchema argument of reportSchema to null.
    • If this or any other reportSchema request fails with a non-2xx response, the edge server should retry after 20 seconds.
  2. If the mutation succeeds, Apollo responds with a ReportSchemaResponse object. This response tells the edge server:

    • How many seconds to wait before sending the next reportSchema request (inSeconds)
    • Whether the next reportSchema request should include the coreSchema that corresponds to the coreSchemaHash provided in the previous request (withCoreSchema)

    If the mutation fails, Apollo responds with a ReportSchemaError object. In this case, the edge server should stop reporting its schema. This error should not cause the server to crash.

    • The message field of ReportSchemaError provides a human-readable message describing the error.
    • Correct the error and deploy a new version of your edge server to resume schema reporting.
  3. Assuming success in step 2, the edge server waits the specified number of seconds, then executes the reportSchema mutation again.

  4. Go to step 2.

Pseudocode example

val schema = normalize("type Query { .. }")
val report = SchemaReport(..)
val withSchema = false
function sendReport() {
val coreSchema = withSchema ? schema : null
val response = reportSchema(coreSchema, report)
if (response.code) {
throw exception
withSchema = response.withExecutableSchema
setTimeout(sendReport, response.inSeconds)

SchemaReport fields

These are the fields of the SchemaReport input type you provide as an argument to the reportSchema mutation.

Name /


Required. A randomly generated UUID that's unique for each instance of your edge server. Set this value on server startup (a given value should not persist across restarts).



Required. The hexadecimal string representation of the normalized schema document's SHA-256 hash.



Required. Indicates which Apollo Studio graph and variant the server is reporting its schema to (e.g., my-graph-id@my-variant). See Graph ref.



An ID that's unique for each instance of your edge server. Unlike bootId, this value should persist across an instance's restarts. In a Kubernetes cluster, this might be the pod name, whereas the container can restart.



An arbitrary string you can set to distinguish data sent by different versions of your edge server. For example, this can be the SHA of the Git commit for your deployed server code. We plan to make this value visible in Apollo Studio.



The runtime that your edge server is running, such as node 12.03.



The name and version of the server and/or reporting agent your edge server is using, such as apollo-server-2.8 or graphql-java-3.1.



The infrastructure environment that your edge server is running in (localhost, kubernetes/deployment, aws lambda, google cloud run, google cloud function, AWS ECS, etc.)

Schema normalization

Two semantically identical schemas can appear different to the schema registry, such as when those schemas list the same set of object fields in a different order. To avoid this scenario, every edge server should normalize its schema before sending it to the registry.

To normalize your schema, do all of the following:

  • Apply stable sorting (such as alphabetical) to the order of all type, field, and argument definitions.
  • Remove all redundant whitespace.
  • Remove all comments (but not docstrings).

Note that runtime dependencies on your schema document might result in poor user experience in tracking your schema changes, or even throttling of service availability.

Type definitions

The schema reporting protocol uses the following GraphQL types, referred to in Protocol sequence above:

type Mutation {
reportSchema(coreSchema: String, report: SchemaReport!): ReportSchemaResult
# This type's fields are documented above.
input SchemaReport {
bootId: String!
coreSchemaHash: String!
graphRef: String!
libraryVersion: String
platform: String
runtimeVersion: String
serverId: String
userVersion: String
interface ReportSchemaResult {
inSeconds: Int!
withCoreSchema: Boolean!
type ReportSchemaResponse implements ReportSchemaResult {
inSeconds: Int!
withCoreSchema: Boolean!
type ReportSchemaError implements ReportSchemaResult {
code: ReportSchemaErrorCode!
inSeconds: Int!
message: String!
withCoreSchema: Boolean!
enum ReportSchemaErrorCode {
Edit on GitHub
Using the Rover CLI
Graphs and variants