CrowBar | GraphQL | Corvus Skip to main content

GraphQL

The Corvus Insurance API is a GraphQL API with a single endpoint. How requests and responses are shaped, the terms the reference pages use, and how to use the API well.

What GraphQL is

The Corvus Insurance API is a GraphQL API. You send every operation to one endpoint, /q, as an HTTP POST with a JSON body. There is no REST endpoint per resource.

GraphQL is a query language for APIs. Each request carries a document that names one operation and lists the fields you want back. The response contains those fields and no others.

How GraphQL differs from REST
TopicRESTThis API (GraphQL)
Endpoints One URL per resource.One endpoint, /q, for every operation.
HTTP method GET, POST, PUT, PATCH or DELETE, by action.POST for every request. A query reads data; a mutation changes it.
Response fields Fixed by the server.Exactly the fields you select.
Errors Reported by the HTTP status code.Reported in an errors array, usually with HTTP 200.

Vocabulary

The terms used on the reference pages.

Query
An operation that reads data and changes nothing. For example, quoteRequests lists quote requests.
Mutation
An operation that creates or changes data. For example, createClient creates a client.
Operation name
The name you give an operation after the query or mutation keyword, such as ListQuoteRequests in query ListQuoteRequests. Send the same name as operationName.
Variables
Typed inputs that the operation declares with $, such as $first: Int!, and that you send separately in the variables object. The Arguments table on each reference page lists them.
Selection set
The fields inside { } that you want back. The response contains these fields and no others. The Response fields table on each reference page lists the fields you can select.
Fragment
A named selection set that you can reuse. Declare it with fragment Name on Type { ... } and include it with ...Name.
Input type
An object type used only as an argument, such as AddressInput. Send it as a JSON object in variables.
Enum
A type with a fixed set of values, such as ProductType (CYBER or TECH_EO). Send an enum value in variables as a JSON string, spelt exactly as listed.
Non-null (!)
A type that ends in !, such as ID!, is never null. On an argument it means the value is required. On a response field it means the field always has a value.

Request shape

  • Method: POST, for queries and mutations alike.
  • Endpoint: https://api-sandbox.corvusinsurance.com/q in the sandbox and https://crowbar.corvusinsurance.com/q in production.
  • Headers: the authentication headers and JSON content headers listed on Authentication.
  • Body: a JSON object with the keys in the Request body keys table. Send one operation per request.
Request body keys
KeyTypeRequiredDescription
query stringRequiredThe GraphQL document: the operation text, as a JSON string.
variables objectOptionalValues for the variables the operation declares. Required when it declares a non-null (!) variable.
operationName stringOptionalThe name of the operation to run. Required when query contains more than one operation.
Copied
POST /q HTTP/1.1Host: api-sandbox.corvusinsurance.comX-API-KEY: <your API key>X-USER-EMAIL: <email of the user making the request>X-USER-NAME: <name of the user making the request>Content-Type: application/jsonAccept: application/json{"query": "query GetQuoteRequest($id: ID!) { quoteRequest(id: $id) { id status } }", "variables": {"id": "9wrvdpN5QaWlJ5PomsPe1g"}, "operationName": "GetQuoteRequest"}

Response shape

The response body is a JSON object with up to two keys:

Response body keys
KeyDescription
data The fields you selected, in the shape of your selection set. null, or left out, when the operation could not run.
errors A list of errors, each with a message and usually locations and path. Left out when there are no errors.
Once a request reaches GraphQL, the status is HTTP 200 even when the operation fails. Check errors on every response. Requests rejected before GraphQL runs, such as a missing API key or a body with no query, return 400, 401 or 404 (see Errors).

Partial success

One request can succeed for some fields and fail for others. The failed field is null, and an entry in errors names it in path. The other fields keep their data.

If the failed field is non-null (!), the null moves up to the nearest field that can be null. For a non-null top-level field, such as quoteRequest, the whole of data is null.

Copied
query Dashboard {  policies(first: 1) {    edges { node { id } }  }  quoteRequests {    edges { node { id } }  }}
Copied
{  "data": {    "policies": {      "edges": [{ "node": { "id": "oP8eyTn2QjeMbWj0rH6xTw" } }]    },    "quoteRequests": null  },  "errors": [    {      "message": "You must either supply `:first` or `:last`",      "path": ["quoteRequests"],      "locations": [{ "line": 5, "column": 3 }]    }  ]}

Invalid documents

If the document cannot be parsed or validated, for example a misspelt field or a missing required variable, nothing runs. The response has errors and no data key, still with HTTP 200.

Copied
{  "errors": [    {      "message": "Cannot query field \"nope\" on type \"QuoteRequestConnection\".",      "locations": [{ "line": 1, "column": 35 }]    }  ]}

Worked example: list quote requests

One complete request and its response.

This query lists the first 25 quote requests with status QUOTES_SHARED for clients whose broker team includes the user in X-USER-EMAIL.

1. Write the operation

Copied
query ListQuoteRequests($first: Int!, $after: String, $statusIn: [QuoteRequestStatus!]) {  quoteRequests(first: $first, after: $after, statusIn: $statusIn) {    pageInfo {      hasNextPage      endCursor    }    edges {      node {        id        status        productType        client {          id          name        }      }    }  }}

2. Put it in the request body

Save the body as request.json. The operation is the query string; its inputs go in variables.

Copied
{  "query": "query ListQuoteRequests($first: Int!, $after: String, $statusIn: [QuoteRequestStatus!]) { quoteRequests(first: $first, after: $after, statusIn: $statusIn) { pageInfo { hasNextPage endCursor } edges { node { id status productType client { id name } } } } }",  "variables": {    "first": 25,    "statusIn": ["QUOTES_SHARED"]  },  "operationName": "ListQuoteRequests"}

3. Send the request

Copied
curl https://api-sandbox.corvusinsurance.com/q \  -X POST \  -H "X-API-KEY: $CORVUS_API_KEY" \  -H "X-USER-EMAIL: broker@example.com" \  -H "X-USER-NAME: Example Broker" \  -H "Content-Type: application/json" \  -H "Accept: application/json" \  --data @request.json

4. Read the response

The keys come back in alphabetical order, not in the order of the selection set. Read each field by name. To get the next page, send the request again with after set to pageInfo.endCursor.

Copied
{  "data": {    "quoteRequests": {      "edges": [        {          "node": {            "client": {              "id": "r_ueqcfTSheKXZJ8zwbk_g",              "name": "Test Client"            },            "id": "9wrvdpN5QaWlJ5PomsPe1g",            "productType": "CYBER",            "status": "QUOTES_SHARED"          }        }      ],      "pageInfo": {        "endCursor": "YXJyYXljb25uZWN0aW9uOjA=",        "hasNextPage": false      }    }  }}

Best practices

Request only the fields you need

Select only the fields your integration uses, and only fields shown in these docs. Some fields are not available to API partners; selecting one returns unauthorized and can null the object that contains it (see Fields not available to API partners).

Pass values as variables

Declare each input on the operation and send its value in variables. Never insert values into the query string. Variables are typed and checked by the API and need no escaping.

Copied
query GetQuoteRequest($id: ID!) {  quoteRequest(id: $id) {    id    status  }}

Name every operation

Give every operation a name, such as query ListQuoteRequests, and send it as operationName. A document with more than one operation and no operationName is rejected. Include the name when you contact support about a request.

Check errors on every response

Treat a response as successful only when errors is missing or empty. HTTP 200 alone does not mean the operation succeeded. See Errors for the common messages and how to read validation errors.

Handle partial results

When a response has both data and errors, use the fields that have values and handle each error by its path. Do not read a null field as "no value" without checking whether an error names it (see Partial success).

Use fragments for repeated selections

When several operations select the same fields of a type, define them once as a fragment and include it with ...Name.

Copied
query ClientQuoteRequests($first: Int!, $clientId: ID!) {  quoteRequests(first: $first, clientId: $clientId) {    edges {      node {        ...QuoteRequestSummary      }    }  }}fragment QuoteRequestSummary on QuoteRequest {  id  status  productType}

Paginate with cursors

  • List operations return a connection. Pass first on every request; leaving it out returns an error.
  • Each list operation has a maximum page size. A larger first is reduced to the maximum without an error.
  • To get the next page, pass pageInfo.endCursor from the last response as after.
  • Stop when hasNextPage is false or a page has no edges. hasNextPage can be true when the next page is empty.
  • Treat cursors as opaque. Do not decode, build or store them.
Copied
query ListQuoteRequests($first: Int!, $after: String) {  quoteRequests(first: $first, after: $after) {    pageInfo {      hasNextPage      endCursor    }    edges {      node {        id      }    }  }}

Retry only requests that can succeed on retry

  • Do not retry a request that failed with a validation error, unauthorized, not_found, or a 400, 401 or 404 status. Fix the request first.
  • Retry network failures, timeouts and 5xx statuses with exponential backoff and a limit on attempts.
  • Set a timeout on every request. Treat a timeout as an unknown outcome, not a failure.
  • You can retry a query safely. Before you retry a mutation whose outcome is unknown, query for the record first: repeating createClient or createQuoteRequest creates a duplicate, and duplicate clients or submissions may delay quote delivery.

Use these docs as the schema reference

The operations and fields documented here are the ones supported for API partners. The schema contains more types and fields than API partners can use, and an undocumented field may return unauthorized or change without notice. Do not discover operations or fields from the schema itself. If you need data that is not documented, contact api@corvusinsurance.com.

Read fields by name and allow for null

Do not depend on the order of keys in a response; read each field by name. A field whose type has no ! can be null. Handle null for every such field you select.

Learn more about GraphQL

The official GraphQL documentation covers the language in depth: