Skip to content

API Reference Overview

!!! info "TL;DR" REST API at https://canviq.app/api. Authenticate with Bearer token. JSON request/response. Paginated results. Standard error format.

Base URL

All API endpoints are prefixed with:

https://canviq.app/api

For local development:

http://localhost:3000/api

Authentication

Endpoints under /api/v1/* and the MCP server require a Bearer token in the Authorization header:

curl https://canviq.app/api/v1/keys \
  -H "Authorization: Bearer your-api-key-here"

Get an API key from the admin dashboard under Settings → API Keys.

Live and test keys are separate

Every key is bound to one environment, named in its prefix: pk_live_… for production, pk_test_… for the sandbox. The two never cross. A test key reads and writes test data only, and that includes the keys themselves — with a test key, GET /api/v1/keys lists only your test keys, and a live key's ID returns 404, the same as an ID that does not exist. POST /api/v1/keys always creates the key in the calling key's own environment; sending an environment that does not match yours is refused with 403.

To manage keys in the other environment, use a key from that environment, or the dashboard, which shows both.

Agent keys are rotated from the dashboard

A key tied to an MCP agent takes its access from that agent's policies rather than from the key itself, so a replacement key carries that agent's access whatever the caller holds. POST /api/v1/keys/{id} with action: "rotate" therefore refuses an agent key with 403. You can still read it with GET and revoke it with DELETE — rotation is the one thing unavailable here. Rotate it from the API keys page in your dashboard instead.

Public vs Authenticated Endpoints

Access Level Endpoints Example
Admin Moderation, analytics, bulk actions POST /api/admin/merge
Agent MCP tools POST /api/mcp/tools

GET /api/submissions requires a logged-in browser session (magic link or GitHub OAuth), not an org API key. It is not part of the Bearer-token surface above; it is called from Canviq's own admin dashboard. There is no public REST API for reading, filtering, or writing submissions, votes, or comments today. See Votes API and Comments API for details.

Content Type

All requests and responses use JSON:

Content-Type: application/json

Response Format

Success Response

{
  "data": {...},
  "meta": {
    "page": 1,
    "limit": 50,
    "total": 123
  }
}

Error Response

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input",
    "details": [
      {
        "field": "title",
        "message": "Title must be at least 5 characters"
      }
    ]
  }
}

HTTP Status Codes

Code Meaning When
200 OK Successful GET, PATCH, DELETE
201 Created Successful POST
400 Bad Request Invalid input, validation failed
401 Unauthorized Missing or invalid API key
403 Forbidden Authenticated but lacking permissions
404 Not Found Resource doesn't exist
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Server error

Pagination

List endpoints return paginated results. Use query parameters to navigate:

GET /api/submissions?page=2&limit=25

Parameters:

  • page — Page number (1-indexed, default: 1)
  • limit — Items per page (default: 20, max: 100)

Response includes pagination metadata:

{
  "data": [...],
  "meta": {
    "page": 2,
    "limit": 25,
    "total": 123,
    "total_pages": 5,
    "has_next": true,
    "has_prev": true
  }
}

Filtering, Sorting, and Field Expansion

GET /api/submissions does not support query-param filtering, a sort parameter, or an expand parameter today. Results are always newest-first.

Rate Limits

Access Level Limit Window
Public 30 requests 1 minute
Authenticated 100 requests 1 minute
Admin 300 requests 1 minute
Agent Tier-based 1 minute

Rate limit headers are included in every response:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1675123456

If rate limited, the server returns 429 with:

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests",
    "retry_after": 42
  }
}

Internationalization

Set the Accept-Language header to get localized content:

curl https://canviq.app/api/submissions \
  -H "Accept-Language: es"

Supported locales: en, es, fr, de, ru, pt-BR, ja, it

Category and tag names are returned in the requested locale if translations exist, otherwise fall back to English.

CORS

CORS is configured per-endpoint:

  • Session-authenticated endpoints (e.g., GET /api/submissions): Require session cookies (sameSite: lax) which are only sent for same-site requests.
  • /api/v1/* and MCP endpoints: Accept Bearer token authentication (no cookie dependency).

!!! warning "CORS is NOT open by default" Unlike some APIs, Canviq does not enable Access-Control-Allow-Origin: *. Cross-origin access must be explicitly configured.

Webhooks

Subscribe to events via webhooks. Configure in admin dashboard under Settings → Webhooks.

Supported events:

  • submission.created
  • submission.updated
  • vote.created
  • comment.created
  • status.changed

What's Next