Skip to content
API Reference

API Reference

Technical documentation for integrating with Aether’s APIs.

Overview

Aether provides multiple API endpoints for integration:

  • GraphQL API - Primary API for all data operations
  • REST API - For webhook receivers and simple integrations
  • Webhooks - Receive notifications for events

Authentication

All API requests require authentication via JWT tokens.

Authentication Flow

1. User authenticates (magic link, OAuth, SSO)
2. Server returns JWT access token
3. Client includes token in Authorization header
4. Client specifies Workspace in X-Aether-Workspace header
5. Server validates and processes request

Headers

Header Required Description
Authorization Bearer <jwt-token>
X-Aether-Workspace ws_<workspace-uuid> for workspace binding
Content-Type application/json for most requests
Idempotency-Key For mutation retries (recommended)
X-Aether-Client Client identifier for tracking

Token Management

  • Access Token TTL: Configurable (default: 1 hour)
  • Refresh Token TTL: Configurable (default: 7 days)
  • Token Type: JWT with claims for account ID, email, etc.
  • Revocation: Tokens can be revoked (logout)

Rate Limits

Aether implements comprehensive rate limiting to protect the platform.

Scope Hierarchy

Rate limits are applied at multiple scopes. The strictest match wins:

Request
  ↓
Per-Session (access token) → Smallest
  ↓
Per-Account (your identity) → Primary authenticated cap
  ↓
Per-Workspace (tenant-scoped) → Automation cap
  ↓
Per-IP (source address) → Backstop (MUST be ≥ Per-Workspace)
  ↓
Per-Endpoint Class → Feature-specific limits

Why this hierarchy?

  • Prevents a single leaked token from overwhelming the system (Per-Session)
  • Prevents multiplying quota across multiple Free Workspaces (Per-Account below Per-Workspace)
  • Ensures single-IP users never trip IP before Workspace (Per-IP above Per-Workspace)

Default Per-Plan Ceilings

Per-Account (Authenticated)

Window Free Entrepreneur Enterprise
Per-second burst 10 30 100
Per-minute 100 600 6,000
Per-hour 1,000 6,000 60,000
Per-day 10,000 60,000 600,000 (soft)

Per-Workspace (Authenticated, Secondary)

Window Free Entrepreneur Enterprise
Per-minute 100 1,000 10,000
Per-hour 1,000 10,000 100,000
Per-day 10,000 100,000 1,000,000 (soft)

Per-IP (Authed + Anonymous Backstop)

Window Free-equivalent Entrepreneur-equivalent Enterprise-equivalent
Per-second burst 20 100 1,000
Per-minute 200 1,200 12,000
Per-hour 2,000 12,000 120,000
Per-day 20,000 120,000 1,200,000 (soft)

Per-Session (Safety Net)

Window All Plans
Per-minute 200
Per-hour 2,000

Mutation Window (Separate Bucket)

Window Free Entrepreneur Enterprise
Per-minute mutations 30 200 2,000
Per-hour mutations 300 2,000 20,000

Endpoint Class Limits

Specific high-cost endpoints have their own quotas:

Endpoint Class Free Entrepreneur Enterprise
INCI Ingredient Import 10/hour 100/hour 1,000/hour
Marketplace Product Sync 60/hour 600/hour
Compliance Doc Generation 5/hour 50/hour 500/hour
Audit Log Export 1/hour 10/hour
Workspace Export 1/week 1/week 5/week

Rate Limit Headers

Every API response includes:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1736500800
X-RateLimit-Scope: account
Header Description
X-RateLimit-Limit Ceiling for the matched scope
X-RateLimit-Remaining Tokens left in current window
X-RateLimit-Reset Unix timestamp when window resets
X-RateLimit-Scope Which scope matched (account, workspace, ip, session)
Retry-After Seconds until retry (on 429 only)

Error Responses

429 Too Many Requests

{
  "errors": [{
    "message": "Rate limit exceeded for scope 'account'. Try again in 12 seconds.",
    "extensions": {
      "code": "rate_limited",
      "scope": "account",
      "scopeId": "acc_...",
      "retryAfterSeconds": 12,
      "resetAt": "2026-07-07T13:50:00Z",
      "limit": 100,
      "window": "minute"
    }
  }]
}

423 Locked (Workspace Suspended)

{
  "errors": [{
    "message": "Workspace is suspended. Contact the Workspace Owner or Aether support.",
    "extensions": {
      "code": "workspace_suspended",
      "workspaceStatus": "Suspended"
    }
  }]
}

Bypass Lanes

Some traffic is NOT subject to user-scope limits:

Lane Identification Limit
Stripe Webhooks HMAC signature verification 100/sec per-event-type
Shopify Webhooks HMAC signature verification 100/sec per-workspace
OAuth Callbacks Path 10/sec per-IP
Health Checks Path (/healthz, /livez, /readyz) Unlimited
Metrics Scrape Path + ops network Unlimited

GraphQL API

Aether’s primary API is GraphQL-based, providing flexible querying for all data.

Schema Overview

The GraphQL schema includes:

# Root types
type Query {
  # Product Development
  ingredient(id: ID!): Ingredient
  ingredients(filter: IngredientFilter, pagination: Pagination): IngredientConnection
  vessel(id: ID!): Vessel
  vessels(filter: VesselFilter, pagination: Pagination): VesselConnection
  mixture(id: ID!): Mixture
  mixtures(filter: MixtureFilter, pagination: Pagination): MixtureConnection
  productTemplate(id: ID!): ProductTemplate
  productTemplates(filter: ProductTemplateFilter, pagination: Pagination): ProductTemplateConnection
  product(id: ID!): Product
  products(filter: ProductFilter, pagination: Pagination): ProductConnection
  
  # Inventory
  stockItem(id: ID!): StockItem
  stockItems(filter: StockItemFilter, pagination: Pagination): StockItemConnection
  supplier(id: ID!): Supplier
  suppliers(filter: SupplierFilter, pagination: Pagination): SupplierConnection
  
  # Compliance
  sds(id: ID!): SafetyDataSheet
  allergenSheet(id: ID!): AllergenSheet
  
  # Ecommerce
  shopifyProduct(id: ID!): ShopifyProduct
  orders(filter: OrderFilter, pagination: Pagination): OrderConnection
  
  # Platform
  workspace(id: ID!): Workspace
  account: Account
  seats: [Seat!]!
}

type Mutation {
  # Product Development
  createIngredient(input: CreateIngredientInput!): Ingredient!
  updateIngredient(id: ID!, input: UpdateIngredientInput!): Ingredient!
  deleteIngredient(id: ID!): Boolean!
  
  createVessel(input: CreateVesselInput!): Vessel!
  updateVessel(id: ID!, input: UpdateVesselInput!): Vessel!
  deleteVessel(id: ID!): Boolean!
  
  createMixture(input: CreateMixtureInput!): Mixture!
  updateMixture(id: ID!, input: UpdateMixtureInput!): Mixture!
  deleteMixture(id: ID!): Boolean!
  
  createProductTemplate(input: CreateProductTemplateInput!): ProductTemplate!
  updateProductTemplate(id: ID!, input: UpdateProductTemplateInput!): ProductTemplate!
  deleteProductTemplate(id: ID!): Boolean!
  
  createProduct(input: CreateProductInput!): Product!
  updateProduct(id: ID!, input: UpdateProductInput!): Product!
  deleteProduct(id: ID!): Boolean!
  
  # Inventory
  createStockItem(input: CreateStockItemInput!): StockItem!
  updateStockItem(id: ID!, input: UpdateStockItemInput!): StockItem!
  adjustStock(id: ID!, quantity: Float!, reason: String): StockItem!
  
  # Compliance
  generateSDS(productId: ID!): SafetyDataSheet!
  generateAllergenSheet(productId: ID!): AllergenSheet!
  generateIngredientList(productId: ID!, targetMarket: Market!): IngredientList!
  
  # Ecommerce
  syncToShopify(productId: ID!): ShopifySyncResult!
  importOrders(channel: Channel!, since: DateTime): ImportResult!
}

type Subscription {
  # Real-time updates
  ingredientUpdated: Ingredient!
  stockLevelChanged: StockItem!
  orderCreated: Order!
}

Query Complexity Limits

To prevent expensive queries, Aether implements a 5-layer defense:

Layer 1: Static Query Cost Ceiling (Parse Time)

Every field has a cost. Queries over the limit are rejected before execution.

Plan Max Single-Query Cost
Free 1,000
Entrepreneur 10,000
Enterprise 100,000

Cost Factors:

Factor Weight Notes
Scalar field 1 Leaf value
Object field 2 One fan-out
List field 5 × estimated cardinality Uses first/last or defaults
Nested traversal × depth multiplier 1.5^depth for recursive types
Mutation × 10 base Heavy operations
Heavy mutation × override See mutation cost table
Subscription × 50 initial, × 10/event Long-lived

Layer 2: Depth Limit (Parse Time)

Plan Max Depth Hard Cap
Free 7 20
Entrepreneur 10 20
Enterprise 15 20

Layer 3: Aliasing & Batch Defense (Parse Time)

Aliased repetitions count as lists. Max batch operations:

Plan Max Operations per Batch
Free 1 (batch disabled)
Entrepreneur 5
Enterprise 25
Hard cap 50

Layer 4: Cost-Based Token Bucket (Execution Time)

Cumulative cost per time window:

Window Free Entrepreneur Enterprise
Per-minute 5,000 50,000 500,000
Per-hour 50,000 500,000 5,000,000
Per-day 500,000 5,000,000 50,000,000 (soft)

Layer 5: Result-Size Cap (Post-Execution)

Plan Max Response Size
Free 1 MB
Entrepreneur 10 MB
Enterprise 100 MB
Hard cap 250 MB

Responses exceeding the cap are truncated with a warning.

Mutation Cost Overrides

Some mutations have higher costs:

Mutation Cost Override Reason
INCI import (single) 50 External service call
INCI import (composite) 200 + 50 per component Recursive resolution
OCR scan submit 500 Async job + vendor call
Marketplace product push 100 API call
Marketplace sync all 1,000 Bulk sync
SDS/Allergen/Label generate 200 each Document generation
Audit export 100 Export job
Workspace export 500 GDPR export
Seat invite/revoke 30 Triggers payments + email
Workspace delete 5,000 Cascading delete
Standard mutations 10 Default

GraphQL Endpoint

POST https://api.aether.com/graphql

Example Query

query GetProduct($id: ID!) {
  product(id: $id) {
    id
    name
    description
    vessel {
      id
      name
      volume
    }
    components {
      ingredient {
        id
        name
        inciName
      }
      percentage
    }
    cogs {
      total
      breakdown {
        item
        cost
      }
    }
  }
}

Example Mutation

mutation CreateIngredient($input: CreateIngredientInput!) {
  createIngredient(input: $input) {
    id
    name
    inciName
    casNumber
    density
    cost
  }
}

REST API

For simpler integrations and webhook receivers.

Endpoints

Method Endpoint Description
GET /api/healthz Health check
GET /api/livez Liveness check
GET /api/readyz Readiness check (pings DB)
POST /webhooks/stripe Stripe webhook receiver
POST /webhooks/shopify Shopify webhook receiver

Stripe Webhook

Receive Stripe events for subscription management.

Endpoint: POST /webhooks/stripe

Headers:

Stripe-Signature: <signature>
Content-Type: application/json

Supported Events:

  • invoice.payment_succeeded
  • invoice.payment_failed
  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • checkout.session.completed

Shopify Webhook

Receive Shopify events for order and product updates.

Endpoint: POST /webhooks/shopify

Headers:

X-Shopify-Hmac-Sha256: <signature>
X-Shopify-Shop-Domain: <shop>.myshopify.com
Content-Type: application/json

Supported Topics:

  • orders/create
  • orders/updated
  • orders/paid
  • orders/fulfilled
  • products/create
  • products/update
  • products/delete
  • inventory_levels/update

Webhooks

Aether can send webhooks to your endpoints for various events.

Configuration

Configure webhooks in your Workspace settings:

  • URL - Your endpoint URL
  • Secret - Secret key for HMAC verification
  • Events - Which events to subscribe to
  • Active - Enable/disable the webhook

Outbound Webhooks

Aether sends webhooks for:

Event Description Payload
product.created Product created in Aether Product data
product.updated Product updated in Aether Product data
product.deleted Product deleted in Aether Product ID
stock.low Stock item reached low threshold StockItem + threshold
stock.empty Stock item out of stock StockItem
order.created Order imported from channel Order data
compliance.issue Compliance issue detected Issue details

Webhook Format

{
  "event": "product.created",
  "timestamp": "2026-07-09T10:00:00Z",
  "workspaceId": "ws_...",
  "data": {
    "id": "prod_...",
    "name": "Lavender Body Spray",
    "...": "..."
  },
  "signature": "sha256=..."
}

Verification

Verify webhook signatures:

const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  
  const received = signature.split('=')[1];
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
}

SDKs

JavaScript/TypeScript SDK

Coming soon - Official SDK for browser and Node.js.

Rust SDK

Coming soon - Official SDK for Rust backend integrations.

OpenAPI Spec

The complete API specification is available for generating client libraries:

# Swagger/OpenAPI 3.0
openapi: 3.0.0
info:
  title: Aether API
  version: 1.0.0
servers:
  - url: https://api.aether.com
    description: Production
  - url: https://staging-api.aether.com
    description: Staging

Best Practices

Rate Limiting

  • Honor Retry-After - Always wait the specified time before retrying
  • Exponential Backoff - Use exponential backoff with jitter for retries
  • Idempotency Keys - Always include for mutations to prevent double-execution
  • Monitor Headers - Track X-RateLimit-Remaining and back off proactively when < 20%
  • Batch Requests - Combine multiple operations into one request when possible

Authentication

  • Store Tokens Securely - Never commit tokens to version control
  • Refresh Proactively - Refresh tokens before they expire
  • Handle 401s - Automatically refresh and retry on token expiration
  • Per-Request Workspace - Specify the Workspace header for each request

Error Handling

  • 429 Retry - Retry after Retry-After or exponential backoff
  • 403 Check - Verify permissions and plan limits
  • 423 Handle - Workspace is suspended, limited functionality available
  • 400 Validate - Check query/mutation syntax

Testing

  • Sandbox Environment - Use staging for development
  • Test Tokens - Use test accounts and workspaces
  • Mock Data - Test with realistic data volumes
  • Rate Limit Testing - Verify your handling of rate limits

Support

API Support

  • Entrepreneur Plan - Email support for API questions
  • Enterprise Plan - Priority support with SLA
  • Custom Plans - Dedicated API support available

Status Page

Check API status at: https://status.aether.com

Changelog

API changes are documented in the API Changelog.

Next Steps