GraphQL Authentication

The GraphQL API uses the same authentication system as the REST API, requiring Bearer token authentication for all requests.

API Key Requirements

Key Format

GraphQL API keys follow the same format as REST API keys:

  • Live keys: cl_live_xxxxxxxxxxxxxxxxxxxxxxxx
  • Test keys: cl_test_xxxxxxxxxxxxxxxxxxxxxxxx

Key Scoping

  • API keys are scoped to specific organizations
  • All GraphQL operations are performed within the context of the API key's organization
  • Cross-organization access is not allowed

Authentication Header

Include the Authorization header with every GraphQL request:

Authorization: Bearer cl_live_your_api_key_here

Authentication Examples

curl

curl -X POST https://api.craneledger.ai/graphql \
  -H "Authorization: Bearer cl_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"query": "query { organization { name } }"}'

JavaScript (graphql-request)

import { GraphQLClient, gql } from 'graphql-request'

const client = new GraphQLClient('https://api.craneledger.ai/graphql', {
  headers: {
    authorization: 'Bearer cl_live_your_api_key_here',
  },
})

const query = gql`
  query {
    organization {
      name
    }
  }
`

const data = await client.request(query)

Python (requests)

import requests

url = "https://api.craneledger.ai/graphql"
headers = {
    "Authorization": "Bearer cl_live_your_api_key_here",
    "Content-Type": "application/json"
}

query = """
query {
  organization {
    name
  }
}
"""

response = requests.post(url, json={"query": query}, headers=headers)
data = response.json()

Apollo Client

import { ApolloClient, InMemoryCache, gql } from '@apollo/client'

const client = new ApolloClient({
  uri: 'https://api.craneledger.ai/graphql',
  cache: new InMemoryCache(),
  headers: {
    authorization: 'Bearer cl_live_your_api_key_here',
  },
})

const GET_ORGANIZATION = gql`
  query {
    organization {
      name
    }
  }
`

const { data } = await client.query({ query: GET_ORGANIZATION })

Error Responses

Missing Authentication

{
  "errors": [
    {
      "message": "Authentication required",
      "extensions": {
        "code": "UNAUTHENTICATED"
      }
    }
  ]
}

Invalid API Key

{
  "errors": [
    {
      "message": "Invalid API key",
      "extensions": {
        "code": "UNAUTHENTICATED"
      }
    }
  ]
}

Expired API Key

{
  "errors": [
    {
      "message": "API key has expired",
      "extensions": {
        "code": "UNAUTHENTICATED"
      }
    }
  ]
}

Disabled API Key

{
  "errors": [
    {
      "message": "API key is disabled",
      "extensions": {
        "code": "UNAUTHENTICATED"
      }
    }
  ]
}

Access Denied

{
  "errors": [
    {
      "message": "Access denied: cannot access other organizations",
      "extensions": {
        "code": "FORBIDDEN"
      }
    }
  ]
}

Rate Limiting

Same Limits as REST API

  • GraphQL requests count toward your API quota
  • Rate limits are enforced per API key
  • Complex queries may consume more credits

Rate Limit Headers

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200

Rate Limit Exceeded

{
  "errors": [
    {
      "message": "Rate limit exceeded",
      "extensions": {
        "code": "RATE_LIMIT_EXCEEDED"
      }
    }
  ]
}

API Key Management

Creating API Keys

API keys are managed through the REST API:

# Create a new API key
curl -X POST https://api.craneledger.ai/api/v1/organizations/{org_id}/api-keys \
  -H "Authorization: Bearer cl_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"name": "GraphQL Integration"}'

Listing API Keys

curl https://api.craneledger.ai/api/v1/organizations/{org_id}/api-keys \
  -H "Authorization: Bearer cl_live_your_api_key_here"

Rotating API Keys

curl -X POST https://api.craneledger.ai/api/v1/organizations/{org_id}/api-keys/{key_id}/rotate \
  -H "Authorization: Bearer cl_live_your_api_key_here"

Deleting API Keys

curl -X DELETE https://api.craneledger.ai/api/v1/organizations/{org_id}/api-keys/{key_id} \
  -H "Authorization: Bearer cl_live_your_api_key_here"

Organization Context

Current Organization

The organization query returns data for the API key's organization:

query {
  organization {
    id
    name
    # This returns data for the authenticated organization
  }
}

Organization-Specific Operations

All operations are automatically scoped to the API key's organization:

# This only shows accounts for the authenticated organization
query {
  organization {
    accounts {
      id
      name
    }
  }
}

Testing with Test Keys

Test Environment

Use test API keys for development and testing:

# Test key usage
curl -X POST https://api.craneledger.ai/graphql \
  -H "Authorization: Bearer cl_test_your_test_key_here" \
  -H "Content-Type: application/json" \
  -d '{"query": "query { organization { name } }"}'

Test Data

  • Test keys work with test data
  • No real financial impact
  • Same API behavior as live keys

Security Best Practices

Key Storage

  • Never expose API keys in client-side code
  • Store keys securely server-side
  • Use environment variables
  • Rotate keys regularly

Key Permissions

  • Create separate keys for different applications
  • Use descriptive names for key identification
  • Delete unused keys immediately

Request Validation

  • Always validate GraphQL queries server-side
  • Use persisted queries for production
  • Implement query complexity limits

Troubleshooting

Common Issues

401 Unauthorized

  • Check API key format
  • Verify key is not expired
  • Ensure key is enabled

403 Forbidden

  • Verify organization access
  • Check if trying to access other organizations' data

Rate Limit Errors

  • Implement exponential backoff
  • Consider query optimization
  • Upgrade API plan if needed

Debug Headers

Include debug headers for troubleshooting:

curl -X POST https://api.craneledger.ai/graphql \
  -H "Authorization: Bearer cl_live_your_api_key_here" \
  -H "X-Debug: true" \
  -H "Content-Type: application/json" \
  -d '{"query": "query { organization { name } }"}'

Need help?

Create a free account to access our support portal. Once signed in, use the Support tab in your dashboard to submit a support ticket — our team typically responds within 24 hours.