API Documentation

Access provider status, incidents, and historical data programmatically.

Contents

Free Public API

Three endpoints need no API key. They are rate limited to 120 requests/minute per IP and built for pipelines and failover rules.

GET /api/healthy/{id}: the pipeline gate

The verdict is the HTTP status code: 200 when every requested service is healthy, 503 otherwise, so curl -f is the entire integration. Gate several services with ?services=claude,openai, and loosen the policy with ?allow=degraded (comma-separated status levels to accept besides operational). Unknown service ids return 404 and an allow value that is not a status level returns 400, so a typo fails your pipeline loudly instead of silently passing.

# exits non-zero unless Claude is operational
curl -fsS https://canivibe.ai/api/healthy/claude

# gate on several providers, tolerating degraded performance
curl -fsS "https://canivibe.ai/api/healthy?services=claude,openai&allow=degraded"

The JSON body carries the per-service verdicts:

{
  "healthy": false,
  "checked_at": "2026-07-07T12:00:00Z",
  "services": [
    { "id": "claude", "status": "degraded", "healthy": false,
      "status_since": "2026-07-07T05:41:12Z" },
    { "id": "openai", "status": "operational", "healthy": true }
  ]
}

GET /api/status/{id}: one service

{
  "id": "claude",
  "name": "Claude (Anthropic)",
  "status": "degraded",
  "description": "Claude Code (degraded_performance)",
  "status_since": "2026-07-07T05:41:12Z",
  "last_checked": "2026-07-07T11:59:40Z"
}

GET /api/status: all services

Everything above in one document, plus overall_status. Cached for 30 seconds; CORS enabled (Access-Control-Allow-Origin: *). Status levels are operational, degraded, partial_outage, major_outage, maintenance and unknown.

Authentication

All API requests require authentication using an API key. API keys are available to Pro and Enterprise subscribers. You can create and manage your API keys from the dashboard.

Using Your API Key

Include your API key using one of these two methods:

Authorization Header (recommended):

Authorization: Bearer civ_your_api_key_here

The "Bearer " prefix is required per RFC 6750.

X-API-Key Header:

X-API-Key: civ_your_api_key_here

Rate Limits

API requests are limited to 1,000 requests per hour per API key.

Rate limit headers are included in all responses:

Endpoints

Base URL: https://canivibe.com/api/v1

GET /providers

List all tracked AI providers with their current status.

Example Response

{
  "providers": [
    {
      "id": "claude",
      "name": "Claude (Anthropic)",
      "status": "operational",
      "last_checked": "2025-01-15T10:30:00Z"
    },
    {
      "id": "openai",
      "name": "OpenAI",
      "status": "operational",
      "last_checked": "2025-01-15T10:30:00Z"
    }
  ]
}
GET /status

Get current status for all or specific providers.

Query Parameters

Parameter Description
provider optional Filter by provider ID. Comma-separated for multiple providers; surrounding spaces are ignored. An unrecognised ID returns 404 unknown_provider rather than an empty result — see GET /api/v1/providers for the full ID list.

Example Request

GET /api/v1/status?provider=claude,openai

Example Response

{
  "overall": "operational",
  "providers": [
    {
      "id": "claude",
      "name": "Claude (Anthropic)",
      "status": "operational",
      "description": "All systems operational",
      "last_checked": "2025-01-15T10:30:00Z"
    }
  ],
  "timestamp": "2025-01-15T10:30:00Z"
}
GET /incidents

Get historical incidents within a date range.

Query Parameters

Parameter Description
provider optional Filter by provider ID. Comma-separated for multiple providers; an unrecognised ID returns 404 unknown_provider.
start optional Start date (YYYY-MM-DD). Defaults to 30 days ago.
end optional End date (YYYY-MM-DD). Defaults to today.

Example Response

{
  "incidents": [
    {
      "id": "inc_123",
      "provider_id": "claude",
      "provider_name": "Claude (Anthropic)",
      "title": "API Performance Degradation",
      "status": "resolved",
      "impact": "minor",
      "started_at": "2025-01-10T14:00:00Z",
      "resolved_at": "2025-01-10T16:30:00Z"
    }
  ],
  "query": {
    "start": "2024-12-16",
    "end": "2025-01-15",
    "provider": null
  }
}
GET /history

Get daily status history for providers.

Query Parameters

Parameter Description
provider optional Filter by provider ID. Comma-separated for multiple providers; an unrecognised ID returns 404 unknown_provider.
days optional Number of days of history (1-90). Defaults to 30.

Example Response

{
  "history": [
    {
      "provider_id": "claude",
      "provider_name": "Claude (Anthropic)",
      "days": [
        {"date": "2025-01-15", "status": "operational"},
        {"date": "2025-01-14", "status": "degraded"},
        {"date": "2025-01-13", "status": "operational"}
      ]
    }
  ],
  "query": {
    "days": 30,
    "provider": null
  }
}

Webhook Signatures

Every webhook delivery is signed so you can verify the payload really came from canivibe. When you add a webhook you receive a signing secret (prefixed whsec_) exactly once — store it like a password; it is never shown again. Webhooks created before signatures shipped have no secret: delete and re-add them to get one.

The X-Canivibe-Signature header

Deliveries carry a signature header in the following form, where t is the unix timestamp at sending time and v1 is a hex-encoded HMAC-SHA256:

X-Canivibe-Signature: t=1756000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

Verifying a delivery

1. Split the header on , and read the t and v1 values.
2. Concatenate the timestamp string, a literal ., and the raw (unparsed) request body.
3. Compute HMAC-SHA256 over that string with your whsec_... secret as the key.
4. Compare the hex digest to v1 with a constant-time comparison.
5. Reject deliveries whose timestamp is older than a few minutes — the signed timestamp is what makes replayed deliveries detectable.

# Python
import hmac, hashlib, time

def verify(header, body_bytes, secret):
    parts = dict(p.split("=", 1) for p in header.split(","))
    signed = parts["t"].encode() + b"." + body_bytes
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    fresh = abs(time.time() - int(parts["t"])) < 300
    return fresh and hmac.compare_digest(expected, parts["v1"])

Slack and Discord webhooks are signed too, but those platforms ignore the header; verification only applies to generic endpoints you host yourself.

Error Handling

The API returns standard HTTP status codes and JSON error responses.

Error Response Format

{
  "error": "Unauthorized",
  "code": "invalid_api_key",
  "message": "Invalid or expired API key"
}

Status Codes

Code Description
200 Success
400 Bad request (invalid parameters)
401 Unauthorized (missing or invalid API key)
403 Forbidden (valid key but insufficient subscription)
429 Rate limit exceeded
500 Internal server error

Error Codes

Code Description
missing_api_key No API key provided in the request
invalid_api_key API key is invalid or has been revoked
subscription_required API access requires a Pro or Enterprise subscription
rate_limit_exceeded Too many requests, try again later