← Back to the API integration overview

REST API reference

Every endpoint the dashboard calls, generated from the live OpenAPI spec. Each operation lists its parameters, request body, response shape, and a copy-pasteable curl example. If a backend route is renamed, this page updates on the next deploy and the hand-written examples on the overview page fail their build-time check.

The machine-readable spec lives at /openapi.json. An interactive Swagger UI is at /docs.

admin

get/admin/demo-workspace

Demo Workspace Status

Return the workspace currently flagged ``is_demo``, or nulls. Surfaces which workspace's recent events are being exposed by the unauthenticated ``GET /stats/feed`` endpoint so an admin can see at a glance whether the homepage's "Live feed · last 24 hours" panel is wired to a real workspace and to whom that workspace belongs.

Responses

  • 200

    Successful Response

    { owner_email: string | null; workspace_id: string | null; workspace_name: string | null }

Try it

curl -X GET "https://api.competitorintelligence.co/admin/demo-workspace" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "owner_email": "string",
  "workspace_id": "string",
  "workspace_name": "string"
}
post/admin/demo-workspace

Grant Demo Workspace

Grant the public-demo flag to the given workspace. Delegates to the same service function the CLI script uses (``app.services.demo_workspace.set_demo_workspace``) so the "single demo source at a time" invariant is enforced uniformly: granting clears the flag from any previously-flagged workspace inside the same transaction. The DB-level partial unique index on ``workspaces(is_demo) WHERE is_demo IS TRUE`` is the backstop. Marking a workspace demo exposes competitor names, event titles, summaries, and source URLs publicly via ``GET /stats/feed``; the confirmation step lives in the admin UI, not in this endpoint.

Request body · required

application/json{ workspace_id: string }

Responses

  • 200

    Successful Response

    { owner_email: string | null; workspace_id: string | null; workspace_name: string | null }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/admin/demo-workspace" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "workspace_id": "00000000-0000-0000-0000-000000000000"
}'
Example response200 · application/json
{
  "owner_email": "string",
  "workspace_id": "string",
  "workspace_name": "string"
}
delete/admin/demo-workspace

Revoke Demo Workspace

Clear the public-demo flag from whichever workspace currently holds it. Idempotent: returns the empty/null shape even when no workspace was flagged. Once revoked, the marketing homepage's "Live feed · last 24 hours" panel falls back to the static placeholder rows on the next 30s poll cycle (see ``frontend/src/data/demo-feed.ts``).

Responses

  • 200

    Successful Response

    { owner_email: string | null; workspace_id: string | null; workspace_name: string | null }

Try it

curl -X DELETE "https://api.competitorintelligence.co/admin/demo-workspace" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "owner_email": "string",
  "workspace_id": "string",
  "workspace_name": "string"
}
get/admin/jobs

List Recent Jobs

Parameters

  • limitquery
    integer

Responses

  • 200

    Successful Response

    array<JobRow>
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/admin/jobs" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "attempts": 0,
    "completed_at": "string",
    "created_at": "string",
    "entity_id": "string",
    "entity_type": "string",
    "id": "string",
    "job_type": "string",
    "last_error": "string",
    "started_at": "string",
    "status": "string"
  }
]
get/admin/scheduler

Scheduler Status

Visibility into the digest scheduler: heartbeat, next runs, recent claims.

Responses

  • 200

    Successful Response

    { daily_hour_utc: integer; enabled: boolean; healthy: boolean; last_tick_at: string | null; next_daily_run_at: string; next_run_at: string; … }

Try it

curl -X GET "https://api.competitorintelligence.co/admin/scheduler" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "daily_hour_utc": 0,
  "enabled": false,
  "healthy": false,
  "last_tick_at": "string",
  "next_daily_run_at": "string",
  "next_run_at": "string",
  "next_weekly_run_at": "string",
  "recent_runs": [
    {
      "enqueued_at": "string",
      "job_id": "string",
      "mark": "string",
      "period_type": "string",
      "scheduled_for": "string"
    }
  ],
  "seconds_since_last_tick": 0,
  "stale_after_seconds": 0,
  "weekly_dow": 0
}
get/admin/sources/failures

List Failed Sources

List the workspace's currently-errored sources. Mirrors the ``/admin/jobs`` clamp (``ge=1, le=200``, default 50) so a workspace in the middle of an outage doesn't ship hundreds of rows to the dashboard. ``offset`` (default 0) lets operators page past the cap during a major outage where the failure count is in the thousands — without it, anything past the first ``limit`` rows would be invisible from the dashboard. We return ``total`` alongside ``items`` so the UI can show "showing X-Y of Z" and wire next/previous controls.

Parameters

  • limitquery
    integer
  • offsetquery
    integer

Responses

  • 200

    Successful Response

    { items: array<FailedSourceRow>; limit: integer; offset: integer; total: integer }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/admin/sources/failures" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "items": [
    {
      "competitor_id": "string",
      "error_message": "string",
      "id": "string",
      "last_error_at": "string",
      "last_success_at": "string",
      "source_type": "string",
      "url": "string"
    }
  ],
  "limit": 0,
  "offset": 0,
  "total": 0
}
get/admin/sources/health

Source Health Summary

Responses

  • 200

    Successful Response

    array<SourceHealthRow>

Try it

curl -X GET "https://api.competitorintelligence.co/admin/sources/health" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "errored": 0,
    "healthy": 0,
    "source_type": "string",
    "total": 0
  }
]

alerts

get/alert-rules

List Alert Rules

Responses

  • 200

    Successful Response

    array<AlertRuleResponse>

Try it

curl -X GET "https://api.competitorintelligence.co/alert-rules" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "competitor_id": "00000000-0000-0000-0000-000000000000",
    "confidence_threshold": 0,
    "delivery_channels_json": [
      "string"
    ],
    "event_types_json": [
      "string"
    ],
    "frequency": "string",
    "id": "00000000-0000-0000-0000-000000000000",
    "is_active": false,
    "is_default": false,
    "name": "string",
    "severity_threshold": "string",
    "workspace_id": "00000000-0000-0000-0000-000000000000"
  }
]
post/alert-rules

Create Alert Rule

Request body · required

application/json{ competitor_id: string | null; confidence_threshold: number; delivery_channels: array<string>; event_types: array<string> | null; frequency: string; severity_threshold: string }

Responses

  • 201

    Successful Response

    { competitor_id: string | null; confidence_threshold: number; delivery_channels_json: array<any>; event_types_json: array<any> | null; frequency: string; id: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/alert-rules" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "competitor_id": "00000000-0000-0000-0000-000000000000",
  "confidence_threshold": 0.5,
  "delivery_channels": [
    "email"
  ],
  "event_types": [
    "string"
  ],
  "frequency": "immediate",
  "severity_threshold": "medium"
}'
Example response201 · application/json
{
  "competitor_id": "00000000-0000-0000-0000-000000000000",
  "confidence_threshold": 0,
  "delivery_channels_json": [
    "string"
  ],
  "event_types_json": [
    "string"
  ],
  "frequency": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "is_active": false,
  "is_default": false,
  "name": "string",
  "severity_threshold": "string",
  "workspace_id": "00000000-0000-0000-0000-000000000000"
}
patch/alert-rules/{rule_id}

Update Alert Rule

Parameters

  • rule_idpath · required
    string

Request body · required

application/json{ competitor_id: string | null; confidence_threshold: number; delivery_channels: array<string>; event_types: array<string> | null; frequency: string; severity_threshold: string }

Responses

  • 200

    Successful Response

    { competitor_id: string | null; confidence_threshold: number; delivery_channels_json: array<any>; event_types_json: array<any> | null; frequency: string; id: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X PATCH "https://api.competitorintelligence.co/alert-rules/{rule_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "competitor_id": "00000000-0000-0000-0000-000000000000",
  "confidence_threshold": 0.5,
  "delivery_channels": [
    "email"
  ],
  "event_types": [
    "string"
  ],
  "frequency": "immediate",
  "severity_threshold": "medium"
}'
Example response200 · application/json
{
  "competitor_id": "00000000-0000-0000-0000-000000000000",
  "confidence_threshold": 0,
  "delivery_channels_json": [
    "string"
  ],
  "event_types_json": [
    "string"
  ],
  "frequency": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "is_active": false,
  "is_default": false,
  "name": "string",
  "severity_threshold": "string",
  "workspace_id": "00000000-0000-0000-0000-000000000000"
}
delete/alert-rules/{rule_id}

Delete Alert Rule

Parameters

  • rule_idpath · required
    string

Responses

  • 204

    Successful Response

  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X DELETE "https://api.competitorintelligence.co/alert-rules/{rule_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"

auth

post/auth/login

Login

Request body · required

application/json{ email: string; password: string }

Responses

  • 200

    Successful Response

    { access_token: string; token_type: string }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/auth/login" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "string",
  "password": "string"
}'
Example response200 · application/json
{
  "access_token": "string",
  "token_type": "bearer"
}
get/auth/me

Me

Responses

  • 200

    Successful Response

    { user: UserResponse; workspace_id: string }

Try it

curl -X GET "https://api.competitorintelligence.co/auth/me" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "user": {
    "created_at": "2026-04-24T12:00:00Z",
    "email": "string",
    "id": "00000000-0000-0000-0000-000000000000",
    "is_admin": false,
    "name": "string"
  },
  "workspace_id": "00000000-0000-0000-0000-000000000000"
}
post/auth/signup

Signup

Request body · required

application/json{ digest_timezone: string | null; email: string; name: string; password: string }

Responses

  • 201

    Successful Response

    { access_token: string; token_type: string }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/auth/signup" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "string",
  "name": "string",
  "password": "string"
}'
Example response201 · application/json
{
  "access_token": "string",
  "token_type": "bearer"
}

chat

post/chat

Chat

Request body · required

application/json{ messages: array<ChatMessage> }

Responses

  • 200

    Successful Response

    any
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/chat" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "messages": [
    {
      "role": "string"
    }
  ]
}'
Example response200 · application/json
"string"

competitors

get/competitors

List Competitors

Responses

  • 200

    Successful Response

    array<CompetitorResponse>

Try it

curl -X GET "https://api.competitorintelligence.co/competitors" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "aliases": [
      "string"
    ],
    "created_at": "2026-04-24T12:00:00Z",
    "description": "string",
    "domain": "string",
    "id": "00000000-0000-0000-0000-000000000000",
    "name": "string",
    "updated_at": "2026-04-24T12:00:00Z",
    "workspace_id": "00000000-0000-0000-0000-000000000000"
  }
]
post/competitors

Create Competitor

Request body · required

application/json{ aliases: array<string> | null; description: string | null; domain: string; name: string }

Responses

  • 201

    Successful Response

    { aliases: array<string> | null; created_at: string; description: string | null; domain: string; id: string; name: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/competitors" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "string",
  "name": "string"
}'
Example response201 · application/json
{
  "aliases": [
    "string"
  ],
  "created_at": "2026-04-24T12:00:00Z",
  "description": "string",
  "domain": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "string",
  "updated_at": "2026-04-24T12:00:00Z",
  "workspace_id": "00000000-0000-0000-0000-000000000000"
}
get/competitors/{competitor_id}

Get Competitor

Parameters

  • competitor_idpath · required
    string

Responses

  • 200

    Successful Response

    { aliases: array<string> | null; created_at: string; description: string | null; domain: string; id: string; name: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/competitors/{competitor_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "aliases": [
    "string"
  ],
  "created_at": "2026-04-24T12:00:00Z",
  "description": "string",
  "domain": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "string",
  "updated_at": "2026-04-24T12:00:00Z",
  "workspace_id": "00000000-0000-0000-0000-000000000000"
}
patch/competitors/{competitor_id}

Update Competitor

Parameters

  • competitor_idpath · required
    string

Request body · required

application/json{ aliases: array<string> | null; description: string | null; domain: string | null; name: string | null }

Responses

  • 200

    Successful Response

    { aliases: array<string> | null; created_at: string; description: string | null; domain: string; id: string; name: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X PATCH "https://api.competitorintelligence.co/competitors/{competitor_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "aliases": [
    "string"
  ],
  "description": "string",
  "domain": "string",
  "name": "string"
}'
Example response200 · application/json
{
  "aliases": [
    "string"
  ],
  "created_at": "2026-04-24T12:00:00Z",
  "description": "string",
  "domain": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "string",
  "updated_at": "2026-04-24T12:00:00Z",
  "workspace_id": "00000000-0000-0000-0000-000000000000"
}
delete/competitors/{competitor_id}

Delete Competitor

Parameters

  • competitor_idpath · required
    string

Responses

  • 204

    Successful Response

  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X DELETE "https://api.competitorintelligence.co/competitors/{competitor_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"

digests

get/digests

List Digests

Responses

  • 200

    Successful Response

    array<DigestResponse>

Try it

curl -X GET "https://api.competitorintelligence.co/digests" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "delivered_at": "2026-04-24T12:00:00Z",
    "delivery_summary": {
      "bounced": 0,
      "complained": 0,
      "delivered": 0,
      "failed": 0,
      "opened": 0,
      "opens": 0,
      "pending": 0,
      "sent": 0,
      "suppressed": 0,
      "total": 0
    },
    "generated_at": "2026-04-24T12:00:00Z",
    "id": "00000000-0000-0000-0000-000000000000",
    "payload_json": {},
    "period_end": "2026-04-24T12:00:00Z",
    "period_start": "2026-04-24T12:00:00Z",
    "period_type": "string",
    "workspace_id": "00000000-0000-0000-0000-000000000000"
  }
]
post/digests

Create Digest

Parameters

  • period_typequery
    string

Responses

  • 201

    Successful Response

    { delivered_at: string | null; delivery_summary: DigestDeliverySummary; generated_at: string; id: string; payload_json: object | null; period_end: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/digests" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response201 · application/json
{
  "delivered_at": "2026-04-24T12:00:00Z",
  "delivery_summary": {
    "bounced": 0,
    "complained": 0,
    "delivered": 0,
    "failed": 0,
    "opened": 0,
    "opens": 0,
    "pending": 0,
    "sent": 0,
    "suppressed": 0,
    "total": 0
  },
  "generated_at": "2026-04-24T12:00:00Z",
  "id": "00000000-0000-0000-0000-000000000000",
  "payload_json": {},
  "period_end": "2026-04-24T12:00:00Z",
  "period_start": "2026-04-24T12:00:00Z",
  "period_type": "string",
  "workspace_id": "00000000-0000-0000-0000-000000000000"
}
get/digests/suppressions

List Suppressions

Suppression list for the caller's workspace only.

Responses

  • 200

    Successful Response

    array<SuppressedEmailResponse>

Try it

curl -X GET "https://api.competitorintelligence.co/digests/suppressions" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "detail": "string",
    "email": "string",
    "reason": "string",
    "suppressed_at": "2026-04-24T12:00:00Z"
  }
]
post/digests/suppressions

Add Suppression

Manually add an address to the workspace suppression list. Idempotent: if the address is already suppressed we return the existing row unchanged so admins can re-submit without seeing an error.

Request body · required

application/json{ detail: string | null; email: string; reason: string }

Responses

  • 201

    Successful Response

    { detail: string | null; email: string; reason: string; suppressed_at: string }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/digests/suppressions" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "string"
}'
Example response201 · application/json
{
  "detail": "string",
  "email": "string",
  "reason": "string",
  "suppressed_at": "2026-04-24T12:00:00Z"
}
delete/digests/suppressions/{email}

Remove Suppression

Remove a suppression entry, scoped to the caller's workspace.

Parameters

  • emailpath · required
    string

Responses

  • 204

    Successful Response

  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X DELETE "https://api.competitorintelligence.co/digests/suppressions/{email}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
post/digests/webhooks/email

Email Provider Webhook

Ingest delivery events from Resend (or compatible Svix-signed provider). Auth: requires ``RESEND_WEBHOOK_SECRET`` to be configured and the request to carry valid ``svix-id`` / ``svix-timestamp`` / ``svix-signature`` headers signed with that secret. Unsigned, mis-signed, or replayed requests are rejected with 401. Fails closed: an unconfigured secret means the endpoint is not accepting traffic.

Responses

  • 202

    Successful Response

    any

Try it

curl -X POST "https://api.competitorintelligence.co/digests/webhooks/email" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response202 · application/json
"string"
get/digests/{digest_id}

Get Digest

Parameters

  • digest_idpath · required
    string

Responses

  • 200

    Successful Response

    { delivered_at: string | null; delivery_summary: DigestDeliverySummary; generated_at: string; id: string; payload_json: object | null; period_end: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/digests/{digest_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "delivered_at": "2026-04-24T12:00:00Z",
  "delivery_summary": {
    "bounced": 0,
    "complained": 0,
    "delivered": 0,
    "failed": 0,
    "opened": 0,
    "opens": 0,
    "pending": 0,
    "sent": 0,
    "suppressed": 0,
    "total": 0
  },
  "generated_at": "2026-04-24T12:00:00Z",
  "id": "00000000-0000-0000-0000-000000000000",
  "payload_json": {},
  "period_end": "2026-04-24T12:00:00Z",
  "period_start": "2026-04-24T12:00:00Z",
  "period_type": "string",
  "workspace_id": "00000000-0000-0000-0000-000000000000"
}
get/digests/{digest_id}/deliveries

List Digest Deliveries

Per-recipient delivery status for a digest (admin view).

Parameters

  • digest_idpath · required
    string

Responses

  • 200

    Successful Response

    array<DigestDeliveryResponse>
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/digests/{digest_id}/deliveries" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "bounce_reason": "string",
    "bounced_at": "2026-04-24T12:00:00Z",
    "delivered_at": "2026-04-24T12:00:00Z",
    "digest_id": "00000000-0000-0000-0000-000000000000",
    "id": "00000000-0000-0000-0000-000000000000",
    "last_event": "string",
    "last_event_at": "2026-04-24T12:00:00Z",
    "opened_at": "2026-04-24T12:00:00Z",
    "opens_count": 0,
    "provider": "string",
    "provider_message_id": "string",
    "recipient_email": "string",
    "recipient_name": "string",
    "sent_at": "2026-04-24T12:00:00Z",
    "status": "string"
  }
]
post/digests/{digest_id}/send

Send Digest

Render and email an existing digest to all workspace members.

Parameters

  • digest_idpath · required
    string

Responses

  • 200

    Successful Response

    { delivered_at: string | null; delivery_summary: DigestDeliverySummary; generated_at: string; id: string; payload_json: object | null; period_end: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/digests/{digest_id}/send" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "delivered_at": "2026-04-24T12:00:00Z",
  "delivery_summary": {
    "bounced": 0,
    "complained": 0,
    "delivered": 0,
    "failed": 0,
    "opened": 0,
    "opens": 0,
    "pending": 0,
    "sent": 0,
    "suppressed": 0,
    "total": 0
  },
  "generated_at": "2026-04-24T12:00:00Z",
  "id": "00000000-0000-0000-0000-000000000000",
  "payload_json": {},
  "period_end": "2026-04-24T12:00:00Z",
  "period_start": "2026-04-24T12:00:00Z",
  "period_type": "string",
  "workspace_id": "00000000-0000-0000-0000-000000000000"
}

discovery

post/competitors/{competitor_id}/discover-sources

Discover Sources

Parameters

  • competitor_idpath · required
    string

Responses

  • 200

    Successful Response

    array<SourceResponse>
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/competitors/{competitor_id}/discover-sources" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "competitor_id": "00000000-0000-0000-0000-000000000000",
    "created_at": "2026-04-24T12:00:00Z",
    "error_message": "string",
    "fetch_frequency_minutes": 0,
    "fetch_method": "string",
    "id": "00000000-0000-0000-0000-000000000000",
    "is_active": false,
    "last_error_at": "2026-04-24T12:00:00Z",
    "last_fetched_at": "2026-04-24T12:00:00Z",
    "last_success_at": "2026-04-24T12:00:00Z",
    "normalized_url": "string",
    "source_type": "string",
    "updated_at": "2026-04-24T12:00:00Z",
    "url": "string"
  }
]

displacement

get/displacement/accounts

List Accounts

Responses

  • 200

    Successful Response

    array<TargetAccountOut>

Try it

curl -X GET "https://api.competitorintelligence.co/displacement/accounts" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "champion_linkedin": "string",
    "champion_name": "string",
    "champion_title": "string",
    "company_name": "string",
    "contract_end_estimate": "2026-04-24T12:00:00Z",
    "created_at": "2026-04-24T12:00:00Z",
    "domain": "string",
    "economic_buyer": "string",
    "employee_band": "string",
    "id": "00000000-0000-0000-0000-000000000000",
    "industry": "string",
    "klue_evidence": "string",
    "klue_source": "string",
    "latest_teardown_at": "2026-04-24T12:00:00Z",
    "next_action": "string",
    "next_action_due": "2026-04-24T12:00:00Z",
    "notes": "string",
    "owner": "string",
    "signal_count": 0,
    "stage": "string",
    "teardown_count": 0,
    "tier": "string",
    "updated_at": "2026-04-24T12:00:00Z"
  }
]
post/displacement/accounts

Create Account

Request body · required

application/json{ champion_linkedin: string | null; champion_name: string | null; champion_title: string | null; company_name: string; contract_end_estimate: string | null; domain: string | null; … }

Responses

  • 201

    Successful Response

    { champion_linkedin: string | null; champion_name: string | null; champion_title: string | null; company_name: string; contract_end_estimate: string | null; created_at: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/displacement/accounts" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "company_name": "string"
}'
Example response201 · application/json
{
  "champion_linkedin": "string",
  "champion_name": "string",
  "champion_title": "string",
  "company_name": "string",
  "contract_end_estimate": "2026-04-24T12:00:00Z",
  "created_at": "2026-04-24T12:00:00Z",
  "domain": "string",
  "economic_buyer": "string",
  "employee_band": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "industry": "string",
  "klue_evidence": "string",
  "klue_source": "string",
  "latest_teardown_at": "2026-04-24T12:00:00Z",
  "next_action": "string",
  "next_action_due": "2026-04-24T12:00:00Z",
  "notes": "string",
  "owner": "string",
  "signal_count": 0,
  "stage": "string",
  "teardown_count": 0,
  "tier": "string",
  "updated_at": "2026-04-24T12:00:00Z"
}
get/displacement/accounts/cadence

Cadence Summary

Return account counts grouped by stage in canonical stage order.

Responses

  • 200

    Successful Response

    array<CadenceSummary>

Try it

curl -X GET "https://api.competitorintelligence.co/displacement/accounts/cadence" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "count": 0,
    "stage": "string"
  }
]
get/displacement/accounts/{account_id}

Get Account

Parameters

  • account_idpath · required
    string

Responses

  • 200

    Successful Response

    { champion_linkedin: string | null; champion_name: string | null; champion_title: string | null; company_name: string; contract_end_estimate: string | null; created_at: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/displacement/accounts/{account_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "champion_linkedin": "string",
  "champion_name": "string",
  "champion_title": "string",
  "company_name": "string",
  "contract_end_estimate": "2026-04-24T12:00:00Z",
  "created_at": "2026-04-24T12:00:00Z",
  "domain": "string",
  "economic_buyer": "string",
  "employee_band": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "industry": "string",
  "klue_evidence": "string",
  "klue_source": "string",
  "latest_teardown_at": "2026-04-24T12:00:00Z",
  "next_action": "string",
  "next_action_due": "2026-04-24T12:00:00Z",
  "notes": "string",
  "owner": "string",
  "signal_count": 0,
  "signals": [],
  "stage": "string",
  "teardown_count": 0,
  "teardowns": [],
  "tier": "string",
  "updated_at": "2026-04-24T12:00:00Z"
}
patch/displacement/accounts/{account_id}

Patch Account

Parameters

  • account_idpath · required
    string

Request body · required

application/json{ champion_linkedin: string | null; champion_name: string | null; champion_title: string | null; contract_end_estimate: string | null; economic_buyer: string | null; klue_evidence: string | null; … }

Responses

  • 200

    Successful Response

    { champion_linkedin: string | null; champion_name: string | null; champion_title: string | null; company_name: string; contract_end_estimate: string | null; created_at: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X PATCH "https://api.competitorintelligence.co/displacement/accounts/{account_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "champion_linkedin": "string",
  "champion_name": "string",
  "champion_title": "string",
  "contract_end_estimate": "2026-04-24T12:00:00Z",
  "economic_buyer": "string",
  "klue_evidence": "string",
  "next_action": "string",
  "next_action_due": "2026-04-24T12:00:00Z",
  "notes": "string",
  "owner": "string",
  "stage": "string",
  "tier": "string"
}'
Example response200 · application/json
{
  "champion_linkedin": "string",
  "champion_name": "string",
  "champion_title": "string",
  "company_name": "string",
  "contract_end_estimate": "2026-04-24T12:00:00Z",
  "created_at": "2026-04-24T12:00:00Z",
  "domain": "string",
  "economic_buyer": "string",
  "employee_band": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "industry": "string",
  "klue_evidence": "string",
  "klue_source": "string",
  "latest_teardown_at": "2026-04-24T12:00:00Z",
  "next_action": "string",
  "next_action_due": "2026-04-24T12:00:00Z",
  "notes": "string",
  "owner": "string",
  "signal_count": 0,
  "stage": "string",
  "teardown_count": 0,
  "tier": "string",
  "updated_at": "2026-04-24T12:00:00Z"
}
delete/displacement/accounts/{account_id}

Delete Account

Parameters

  • account_idpath · required
    string

Responses

  • 204

    Successful Response

  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X DELETE "https://api.competitorintelligence.co/displacement/accounts/{account_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
post/displacement/accounts/{account_id}/signals

Add Signal

Parameters

  • account_idpath · required
    string

Request body · required

application/json{ detail: string | null; headline: string; kind: string; occurred_at: string | null; source_url: string | null }

Responses

  • 201

    Successful Response

    { detail: string | null; headline: string; id: string; kind: string; occurred_at: string; source_url: string | null }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/displacement/accounts/{account_id}/signals" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "headline": "string",
  "kind": "string"
}'
Example response201 · application/json
{
  "detail": "string",
  "headline": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "kind": "string",
  "occurred_at": "2026-04-24T12:00:00Z",
  "source_url": "string"
}
post/displacement/accounts/{account_id}/teardown

Create Teardown

Generate a fresh Klue Teardown for this account via Kimi. Idempotency: every call produces a new versioned row (so revising context on the account → re-generate is the workflow).

Parameters

  • account_idpath · required
    string

Responses

  • 201

    Successful Response

    { account_id: string; content_md: string; generated_at: string; id: string; model: string | null; token_usage: integer | null }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/displacement/accounts/{account_id}/teardown" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response201 · application/json
{
  "account_id": "00000000-0000-0000-0000-000000000000",
  "content_md": "string",
  "generated_at": "2026-04-24T12:00:00Z",
  "id": "00000000-0000-0000-0000-000000000000",
  "model": "string",
  "token_usage": 0
}
get/displacement/accounts/{account_id}/teardowns

List Teardowns

Parameters

  • account_idpath · required
    string

Responses

  • 200

    Successful Response

    array<TeardownOut>
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/displacement/accounts/{account_id}/teardowns" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "account_id": "00000000-0000-0000-0000-000000000000",
    "content_md": "string",
    "generated_at": "2026-04-24T12:00:00Z",
    "id": "00000000-0000-0000-0000-000000000000",
    "model": "string",
    "token_usage": 0
  }
]
get/displacement/stories

List Stories

Responses

  • 200

    Successful Response

    array<SwitcherStoryOut>

Try it

curl -X GET "https://api.competitorintelligence.co/displacement/stories" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "account_id": "00000000-0000-0000-0000-000000000000",
    "champion_name": "string",
    "champion_title": "string",
    "closed_at": "2026-04-24T12:00:00Z",
    "company_name": "string",
    "created_at": "2026-04-24T12:00:00Z",
    "id": "00000000-0000-0000-0000-000000000000",
    "public_url": "string",
    "quantified_outcome": "string",
    "status": "string",
    "why_left_klue": "string"
  }
]
post/displacement/stories

Create Story

Request body · required

application/json{ account_id: string | null; champion_name: string | null; champion_title: string | null; closed_at: string | null; company_name: string; public_url: string | null; … }

Responses

  • 201

    Successful Response

    { account_id: string | null; champion_name: string | null; champion_title: string | null; closed_at: string | null; company_name: string; created_at: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/displacement/stories" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "company_name": "string"
}'
Example response201 · application/json
{
  "account_id": "00000000-0000-0000-0000-000000000000",
  "champion_name": "string",
  "champion_title": "string",
  "closed_at": "2026-04-24T12:00:00Z",
  "company_name": "string",
  "created_at": "2026-04-24T12:00:00Z",
  "id": "00000000-0000-0000-0000-000000000000",
  "public_url": "string",
  "quantified_outcome": "string",
  "status": "string",
  "why_left_klue": "string"
}
patch/displacement/stories/{story_id}

Patch Story

Parameters

  • story_idpath · required
    string

Request body · required

application/json{ account_id: string | null; champion_name: string | null; champion_title: string | null; closed_at: string | null; company_name: string; public_url: string | null; … }

Responses

  • 200

    Successful Response

    { account_id: string | null; champion_name: string | null; champion_title: string | null; closed_at: string | null; company_name: string; created_at: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X PATCH "https://api.competitorintelligence.co/displacement/stories/{story_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "company_name": "string"
}'
Example response200 · application/json
{
  "account_id": "00000000-0000-0000-0000-000000000000",
  "champion_name": "string",
  "champion_title": "string",
  "closed_at": "2026-04-24T12:00:00Z",
  "company_name": "string",
  "created_at": "2026-04-24T12:00:00Z",
  "id": "00000000-0000-0000-0000-000000000000",
  "public_url": "string",
  "quantified_outcome": "string",
  "status": "string",
  "why_left_klue": "string"
}
delete/displacement/stories/{story_id}

Delete Story

Parameters

  • story_idpath · required
    string

Responses

  • 204

    Successful Response

  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X DELETE "https://api.competitorintelligence.co/displacement/stories/{story_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"

events

get/events

List Events

Parameters

  • competitor_idquery
    string | null
  • event_typequery
    string | null
  • severityquery
    string | null
  • min_confidencequery
    number | null
  • searchquery
    string | null
  • sincequery
    string | null

    Only return events detected at or after this timestamp.

  • offsetquery
    integer
  • limitquery
    integer

Responses

  • 200

    Successful Response

    array<EventResponse>
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/events" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "competitor_id": "00000000-0000-0000-0000-000000000000",
    "confidence_reasoning": "string",
    "confidence_score": 0,
    "created_at": "2026-04-24T12:00:00Z",
    "detected_at": "2026-04-24T12:00:00Z",
    "diff_id": "00000000-0000-0000-0000-000000000000",
    "event_type": "string",
    "id": "00000000-0000-0000-0000-000000000000",
    "impact_summary": "string",
    "is_inferred": false,
    "observed_at": "2026-04-24T12:00:00Z",
    "severity": "string",
    "source_id": "00000000-0000-0000-0000-000000000000",
    "status": "string",
    "summary": "string",
    "tags_json": [
      "string"
    ],
    "title": "string"
  }
]
get/events/activity

List Activity

Return per-competitor event markers for the past ``days`` days. Powers the small sparkline shown on each competitor card. One workspace- scoped query feeds every card, so the list page avoids N+1 fetches. Only ``detected_at`` and ``severity`` are returned — the chart never needs more than that for the sparkline variant. Competitors with no recent events are still included with an empty list so the frontend can render a flat baseline instead of a missing card.

Parameters

  • daysquery
    integer

Responses

  • 200

    Successful Response

    { competitors: object; days: integer }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/events/activity" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "competitors": {},
  "days": 0
}
get/events/{event_id}

Get Event

Parameters

  • event_idpath · required
    string

Responses

  • 200

    Successful Response

    { competitor_name: string | null; diff_json: object | null; event: EventResponse; evidence: array<EvidenceResponse>; source_url: string | null }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/events/{event_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "competitor_name": "string",
  "diff_json": {},
  "event": {
    "competitor_id": "00000000-0000-0000-0000-000000000000",
    "confidence_reasoning": "string",
    "confidence_score": 0,
    "created_at": "2026-04-24T12:00:00Z",
    "detected_at": "2026-04-24T12:00:00Z",
    "diff_id": "00000000-0000-0000-0000-000000000000",
    "event_type": "string",
    "id": "00000000-0000-0000-0000-000000000000",
    "impact_summary": "string",
    "is_inferred": false,
    "observed_at": "2026-04-24T12:00:00Z",
    "severity": "string",
    "source_id": "00000000-0000-0000-0000-000000000000",
    "status": "string",
    "summary": "string",
    "tags_json": [
      "string"
    ],
    "title": "string"
  },
  "evidence": [
    {
      "asset_object_key": "string",
      "created_at": "2026-04-24T12:00:00Z",
      "evidence_type": "string",
      "excerpt": "string",
      "id": "00000000-0000-0000-0000-000000000000",
      "metadata_json": {},
      "source_url": "string",
      "title": "string"
    }
  ],
  "source_url": "string"
}
patch/events/{event_id}/status

Update Event Status

Parameters

  • event_idpath · required
    string
  • statusquery · required
    string

Responses

  • 200

    Successful Response

    { id: string; status: string }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X PATCH "https://api.competitorintelligence.co/events/{event_id}/status?status=value" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "id": "string",
  "status": "string"
}

gtmvp

post/v1/workspaces

Create Workspace

Create a workspace from a Stripe-paid Engine customer. Idempotent on `customer_email`: if a workspace already exists with the same gtmvp_customer_email, we return it unchanged. This protects against duplicate Stripe webhook fires.

Request body · required

application/json{ company_name: string; customer_email: string; external_audit_id: string | null; industry: string | null; website_url: string | null }

Responses

  • 201

    Successful Response

    { company_name: string | null; created_at: string; customer_email: string | null; id: string; industry: string | null; status: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/v1/workspaces" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "company_name": "string",
  "customer_email": "string"
}'
Example response201 · application/json
{
  "company_name": "string",
  "created_at": "2026-04-24T12:00:00Z",
  "customer_email": "string",
  "id": "string",
  "industry": "string",
  "status": "string",
  "website_url": "string"
}
get/v1/workspaces/{workspace_id}

Get Workspace

Parameters

  • workspace_idpath · required
    string

Responses

  • 200

    Successful Response

    { company_name: string | null; created_at: string; customer_email: string | null; id: string; industry: string | null; status: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/v1/workspaces/{workspace_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "company_name": "string",
  "created_at": "2026-04-24T12:00:00Z",
  "customer_email": "string",
  "id": "string",
  "industry": "string",
  "status": "string",
  "website_url": "string"
}
post/v1/workspaces/{workspace_id}/alerts/subscribe

Subscribe Alerts

Configure alert routing (email/slack) for a workspace. Maps to RivalScope's existing AlertRule model. Replaces any previously-configured GTMVP alert rule (name='__gtmvp_default__') so this endpoint is idempotent under repeated subscription updates.

Parameters

  • workspace_idpath · required
    string

Request body · required

application/json{ email: array<string> | null; min_severity: string; monitor_types: array<string> | null; slack_webhook_url: string | null; workspace_id: string }

Responses

  • 200

    Successful Response

    { ok: boolean }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/v1/workspaces/{workspace_id}/alerts/subscribe" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "workspace_id": "string"
}'
Example response200 · application/json
{
  "ok": true
}
get/v1/workspaces/{workspace_id}/digest

Get Digest

Synthesize a digest from the events in the period. v1 doesn't depend on the existing `digests` table — that table is written by RivalScope's first-party digest scheduler with a different shape. Instead we build a markdown summary live from the period's events, grouped by GTMVP monitor_type.

Parameters

  • workspace_idpath · required
    string
  • periodquery
    string

Responses

  • 200

    Successful Response

    { event_count: integer; id: string; period_end: string; period_key: string; period_start: string; sections: array<DigestSectionOut>; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/v1/workspaces/{workspace_id}/digest" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "event_count": 0,
  "id": "string",
  "period_end": "2026-04-24T12:00:00Z",
  "period_key": "string",
  "period_start": "2026-04-24T12:00:00Z",
  "sections": [
    {
      "body_markdown": "string",
      "heading": "string",
      "highlight_event_ids": [
        "string"
      ],
      "monitor_type": "string"
    }
  ],
  "summary_markdown": "string",
  "workspace_id": "string"
}
get/v1/workspaces/{workspace_id}/events

List Events

Parameters

  • workspace_idpath · required
    string
  • monitor_typequery
    string | null
  • sincequery
    string | null
  • min_severityquery
    string | null
  • limitquery
    integer

Responses

  • 200

    Successful Response

    { events: array<MonitorEventOut>; next_cursor: string | null }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/v1/workspaces/{workspace_id}/events" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "events": [
    {
      "confidence": 0,
      "detected_at": "2026-04-24T12:00:00Z",
      "evidence": [
        {
          "captured_at": "2026-04-24T12:00:00Z",
          "excerpt": "string",
          "title": "string",
          "url": "string"
        }
      ],
      "id": "string",
      "monitor_id": "string",
      "monitor_type": "string",
      "severity": "string",
      "subject": "string",
      "summary": "string",
      "title": "string",
      "workspace_id": "string"
    }
  ],
  "next_cursor": "string"
}
post/v1/workspaces/{workspace_id}/monitors

Register Monitor

Register one of the eight monitor types on a workspace. Idempotent: if a monitor of this type already exists for this workspace, returns it (optionally updating label/cadence/enabled from the payload). Eight rows max per workspace, enforced by the UNIQUE constraint on (workspace_id, monitor_type).

Parameters

  • workspace_idpath · required
    string

Request body · required

application/json{ cadence: string | null; config_json: object | null; enabled: boolean | null; label: string | null; monitor_type: string; workspace_id: string }

Responses

  • 201

    Successful Response

    { cadence: string; config_json: object | null; enabled: boolean; id: string; label: string | null; last_event_at: string | null; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/v1/workspaces/{workspace_id}/monitors" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "monitor_type": "string",
  "workspace_id": "string"
}'
Example response201 · application/json
{
  "cadence": "string",
  "config_json": {},
  "enabled": false,
  "id": "string",
  "label": "string",
  "last_event_at": "2026-04-24T12:00:00Z",
  "last_run_at": "2026-04-24T12:00:00Z",
  "monitor_type": "string",
  "workspace_id": "string"
}
get/v1/workspaces/{workspace_id}/monitors/health

List Monitor Health

Return one health row per registered monitor on the workspace. Joins through `competitors` to pull events_24h since events are keyed on competitor_id, not workspace_id directly.

Parameters

  • workspace_idpath · required
    string

Responses

  • 200

    Successful Response

    array<MonitorHealthOut>
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/v1/workspaces/{workspace_id}/monitors/health" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "consecutive_failures": 0,
    "events_24h": 0,
    "healthy": false,
    "last_event_at": "2026-04-24T12:00:00Z",
    "last_run_at": "2026-04-24T12:00:00Z",
    "monitor_id": "string",
    "monitor_type": "string",
    "status_text": "string"
  }
]

health

get/health

Health Check

Responses

  • 200

    Successful Response

    { service: string; status: string }

Try it

curl -X GET "https://api.competitorintelligence.co/health" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "service": "string",
  "status": "string"
}

insights

get/insights

Get Insights

Get aggregated intelligence for all competitors in the workspace.

Responses

  • 200

    Successful Response

    { competitors: array<CompetitorInsight> }

Try it

curl -X GET "https://api.competitorintelligence.co/insights" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "competitors": [
    {
      "business_model": "string",
      "description": "string",
      "domain": "string",
      "event_count": 0,
      "hiring": {},
      "id": "string",
      "industry": "string",
      "name": "string",
      "pricing": {},
      "product": {},
      "profile": {},
      "recent_events": [
        {
          "confidence_score": 0,
          "detected_at": "string",
          "event_type": "string",
          "id": "string",
          "severity": "string",
          "summary": "string",
          "title": "string"
        }
      ],
      "scanned_sources": 0,
      "total_sources": 0
    }
  ]
}
post/insights/reanalyze/{competitor_id}

Reanalyze Competitor

Re-run Kimi intelligence extraction on all existing snapshots for a competitor.

Parameters

  • competitor_idpath · required
    string

Responses

  • 200

    Successful Response

    { competitor: string | null; error: string | null; events_generated: integer | null }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/insights/reanalyze/{competitor_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "competitor": "string",
  "error": "string",
  "events_generated": 0
}
get/insights/{competitor_id}

Get Competitor Insight

Get detailed intelligence for a single competitor.

Parameters

  • competitor_idpath · required
    string

Responses

  • 200

    Successful Response

    CompetitorInsight | CompetitorNotFoundResponse
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/insights/{competitor_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "business_model": "string",
  "description": "string",
  "domain": "string",
  "event_count": 0,
  "hiring": {},
  "id": "string",
  "industry": "string",
  "name": "string",
  "pricing": {},
  "product": {},
  "profile": {},
  "recent_events": [
    {
      "confidence_score": 0,
      "detected_at": "string",
      "event_type": "string",
      "id": "string",
      "severity": "string",
      "summary": "string",
      "title": "string"
    }
  ],
  "scanned_sources": 0,
  "total_sources": 0
}

marketing

post/marketing/events

Record Marketing Event

Record one client-side marketing analytics event. No DB write — the signal lands in the application log as a single structured line so operators can grep it from the admin log-aggregation surface without paying per-click DB cost. Returns 204 so ``navigator.sendBeacon`` (which discards the response body) can finish quickly and the visitor's click never feels delayed.

Request body · required

application/json{ event: string; event_id: string | null; location: string; source_hostname: string | null }

Responses

  • 204

    Successful Response

  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/marketing/events" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "event": "string",
  "location": "string"
}'

plan

post/beta-claims

Create Beta Claim

Request body · required

application/json{ billing_cycle: string; contact_email: string; contact_name: string | null; notes: string | null; requested_tier: string; seat_count: integer; … }

Responses

  • 201

    Successful Response

    { id: string; message: string; requested_tier: string; seat_count: integer }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/beta-claims" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "contact_email": "string",
  "requested_tier": "string"
}'
Example response201 · application/json
{
  "id": "00000000-0000-0000-0000-000000000000",
  "message": "string",
  "requested_tier": "string",
  "seat_count": 0
}
post/billing/checkout

Start Checkout

Request body · required

application/json{ billing_cycle: string; plan: string; seat_count: integer }

Responses

  • 200

    Successful Response

    { checkout_url: string; session_id: string }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/billing/checkout" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "plan": "string"
}'
Example response200 · application/json
{
  "checkout_url": "string",
  "session_id": "string"
}
post/billing/portal

Open Billing Portal

Return a short-lived Stripe Billing Portal URL for the workspace. Returns 409 if the workspace has no `stripe_customer_id` yet — i.e. they have never completed a Checkout session and there is nothing to manage.

Responses

  • 200

    Successful Response

    { portal_url: string }

Try it

curl -X POST "https://api.competitorintelligence.co/billing/portal" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "portal_url": "string"
}
post/billing/webhook

Stripe Webhook

Parameters

  • Stripe-Signatureheader
    string | null

Responses

  • 200

    Successful Response

    any
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/billing/webhook" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
"string"
get/me/plan

Get My Plan

Responses

  • 200

    Successful Response

    { beta_claimed_at: string | null; beta_grandfathered: boolean; billing: PlanBilling; limits: PlanLimits; plan: string; plan_label: string; … }

Try it

curl -X GET "https://api.competitorintelligence.co/me/plan" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "beta_claimed_at": "string",
  "beta_grandfathered": false,
  "billing": {
    "current_period_end": "string",
    "stripe_customer_id": "string",
    "stripe_subscription_id": "string",
    "subscription_status": "string"
  },
  "limits": {
    "api_access": false,
    "chat_quota": 0,
    "competitors": 0,
    "daily_digest": false,
    "history_days": 0,
    "realtime": false,
    "seats": 0,
    "slack": false
  },
  "plan": "string",
  "plan_label": "string",
  "usage": {
    "chat_used": 0,
    "competitors": 0,
    "seats": 0
  }
}
get/pricing/plans

Public Pricing Plans

Read-only catalog used by the public marketing pricing page.

Responses

  • 200

    Successful Response

    { plans: array<PublicPlanEntry> }

Try it

curl -X GET "https://api.competitorintelligence.co/pricing/plans" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "plans": [
    {
      "api_access": false,
      "beta_price_per_seat_monthly": 0,
      "chat_quota": 0,
      "competitors": 0,
      "daily_digest": false,
      "history_days": 0,
      "label": "string",
      "list_price_per_seat_monthly": 0,
      "plan": "string",
      "realtime": false,
      "seats": 0,
      "slack": false
    }
  ]
}

preferences

get/me/preferences

Get Preferences

Responses

  • 200

    Successful Response

    { digest_frequency: string; digest_hour_local: integer; digest_timezone: string; digest_weekly_dow: integer }

Try it

curl -X GET "https://api.competitorintelligence.co/me/preferences" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "digest_frequency": "string",
  "digest_hour_local": 0,
  "digest_timezone": "string",
  "digest_weekly_dow": 0
}
patch/me/preferences

Update Preferences

Request body · required

application/json{ digest_frequency: string | null; digest_hour_local: integer | null; digest_timezone: string | null; digest_weekly_dow: integer | null }

Responses

  • 200

    Successful Response

    { digest_frequency: string; digest_hour_local: integer; digest_timezone: string; digest_weekly_dow: integer }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X PATCH "https://api.competitorintelligence.co/me/preferences" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "digest_frequency": "string",
  "digest_hour_local": 0,
  "digest_timezone": "string",
  "digest_weekly_dow": 0
}'
Example response200 · application/json
{
  "digest_frequency": "string",
  "digest_hour_local": 0,
  "digest_timezone": "string",
  "digest_weekly_dow": 0
}

profile

post/competitors/{competitor_id}/add-enriched-sources

Add Enriched Sources

Add selected enriched sources to a competitor's monitoring.

Parameters

  • competitor_idpath · required
    string

Request body · required

application/json{ sources: array<object> }

Responses

  • 200

    Successful Response

    { added: integer; sources: array<AddedSource> }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/competitors/{competitor_id}/add-enriched-sources" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "sources": [
    {}
  ]
}'
Example response200 · application/json
{
  "added": 0,
  "sources": [
    {
      "id": "string",
      "source_type": "string",
      "url": "string"
    }
  ]
}
post/competitors/{competitor_id}/profile

Profile Competitor

Classify a competitor's business and discover enriched sources across the web.

Parameters

  • competitor_idpath · required
    string

Responses

  • 200

    Successful Response

    { apify_data: object | null; competitor_id: string; domain: string; enriched_sources: array<EnrichedSourceResponse>; name: string; profile: object; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/competitors/{competitor_id}/profile" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "apify_data": {},
  "competitor_id": "string",
  "domain": "string",
  "enriched_sources": [
    {
      "description": "string",
      "relevance": "string",
      "source_type": "string",
      "title": "string",
      "url": "string",
      "verified": false
    }
  ],
  "name": "string",
  "profile": {},
  "recommendations": [
    {}
  ],
  "website_sources": [
    {}
  ]
}

sources

get/competitors/{competitor_id}/sources

List Sources

Parameters

  • competitor_idpath · required
    string

Responses

  • 200

    Successful Response

    array<SourceResponse>
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/competitors/{competitor_id}/sources" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "competitor_id": "00000000-0000-0000-0000-000000000000",
    "created_at": "2026-04-24T12:00:00Z",
    "error_message": "string",
    "fetch_frequency_minutes": 0,
    "fetch_method": "string",
    "id": "00000000-0000-0000-0000-000000000000",
    "is_active": false,
    "last_error_at": "2026-04-24T12:00:00Z",
    "last_fetched_at": "2026-04-24T12:00:00Z",
    "last_success_at": "2026-04-24T12:00:00Z",
    "normalized_url": "string",
    "source_type": "string",
    "updated_at": "2026-04-24T12:00:00Z",
    "url": "string"
  }
]
post/competitors/{competitor_id}/sources

Create Source

Parameters

  • competitor_idpath · required
    string

Request body · required

application/json{ fetch_frequency_minutes: integer; fetch_method: string; source_type: string; url: string }

Responses

  • 201

    Successful Response

    { competitor_id: string; created_at: string; error_message: string | null; fetch_frequency_minutes: integer; fetch_method: string; id: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/competitors/{competitor_id}/sources" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "source_type": "string",
  "url": "string"
}'
Example response201 · application/json
{
  "competitor_id": "00000000-0000-0000-0000-000000000000",
  "created_at": "2026-04-24T12:00:00Z",
  "error_message": "string",
  "fetch_frequency_minutes": 0,
  "fetch_method": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "is_active": false,
  "last_error_at": "2026-04-24T12:00:00Z",
  "last_fetched_at": "2026-04-24T12:00:00Z",
  "last_success_at": "2026-04-24T12:00:00Z",
  "normalized_url": "string",
  "source_type": "string",
  "updated_at": "2026-04-24T12:00:00Z",
  "url": "string"
}
patch/sources/{source_id}

Update Source

Parameters

  • source_idpath · required
    string

Request body · required

application/json{ fetch_frequency_minutes: integer | null; fetch_method: string | null; is_active: boolean | null; source_type: string | null; url: string | null }

Responses

  • 200

    Successful Response

    { competitor_id: string; created_at: string; error_message: string | null; fetch_frequency_minutes: integer; fetch_method: string; id: string; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X PATCH "https://api.competitorintelligence.co/sources/{source_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "fetch_frequency_minutes": 0,
  "fetch_method": "string",
  "is_active": false,
  "source_type": "string",
  "url": "string"
}'
Example response200 · application/json
{
  "competitor_id": "00000000-0000-0000-0000-000000000000",
  "created_at": "2026-04-24T12:00:00Z",
  "error_message": "string",
  "fetch_frequency_minutes": 0,
  "fetch_method": "string",
  "id": "00000000-0000-0000-0000-000000000000",
  "is_active": false,
  "last_error_at": "2026-04-24T12:00:00Z",
  "last_fetched_at": "2026-04-24T12:00:00Z",
  "last_success_at": "2026-04-24T12:00:00Z",
  "normalized_url": "string",
  "source_type": "string",
  "updated_at": "2026-04-24T12:00:00Z",
  "url": "string"
}
delete/sources/{source_id}

Delete Source

Parameters

  • source_idpath · required
    string

Responses

  • 204

    Successful Response

  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X DELETE "https://api.competitorintelligence.co/sources/{source_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
post/sources/{source_id}/run-now

Run Source Now

Parameters

  • source_idpath · required
    string

Responses

  • 202

    Successful Response

    { job_id: string; status: string }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/sources/{source_id}/run-now" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response202 · application/json
{
  "job_id": "string",
  "status": "string"
}

stats

get/stats/feed

Demo Feed

Return recent events from the workspace flagged as `is_demo`. Unauthenticated on purpose: only the events from a workspace that an operator has *explicitly* opted in to publishing (via ``backend/scripts/set_demo_workspace.py``) are exposed. When no workspace is marked demo, returns an empty payload — the homepage treats that as 'show the static placeholder rows'.

Parameters

  • limitquery
    integer

    Maximum number of events to return. Defaults to 4 (the homepage card has room for that many rows); capped at 10.

Responses

  • 200

    Successful Response

    { competitor_names: array<string>; events: array<DemoFeedEventResponse>; window_hours: integer }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/stats/feed" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "competitor_names": [
    "string"
  ],
  "events": [
    {
      "competitor_name": "string",
      "confidence": 0,
      "detected_at": "2026-04-24T12:00:00Z",
      "event_type": "string",
      "id": "string",
      "severity": "string",
      "source_label": "string",
      "source_url": "string",
      "summary": "string",
      "tag": "string",
      "title": "string"
    }
  ],
  "window_hours": 0
}
get/stats/hero

Hero Stat

Return the proof figure shown next to the homepage CTAs. A `null` ``value`` means we don't have enough data to publish an honest number; the marketing site falls back to ``heroProofStat``.

Responses

  • 200

    Successful Response

    { label: string; median_hours: number | null; metric: string; sample_size: integer; value: string | null; window_days: integer }

Try it

curl -X GET "https://api.competitorintelligence.co/stats/hero" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "label": "string",
  "median_hours": 0,
  "metric": "string",
  "sample_size": 0,
  "value": "string",
  "window_days": 0
}

support

post/support/admin/setup

Admin Setup

Idempotently provision the support inbox and register the webhook. Safe to call repeatedly — both AgentMail calls are no-ops when the inbox or webhook already exists.

Responses

  • 200

    Successful Response

    { inbox: object; webhook: object; webhook_url: string }

Try it

curl -X POST "https://api.competitorintelligence.co/support/admin/setup" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "inbox": {},
  "webhook": {},
  "webhook_url": "string"
}
post/support/admin/sync

Admin Sync

Backfill: fetch the inbox and ingest any messages we don't yet have.

Responses

  • 200

    Successful Response

    any

Try it

curl -X POST "https://api.competitorintelligence.co/support/admin/sync" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
"string"
get/support/threads

List Threads

Responses

  • 200

    Successful Response

    array<SupportThreadResponse>

Try it

curl -X GET "https://api.competitorintelligence.co/support/threads" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
[
  {
    "category": "string",
    "created_at": "2026-04-24T12:00:00Z",
    "id": "00000000-0000-0000-0000-000000000000",
    "last_message_at": "2026-04-24T12:00:00Z",
    "latest_draft": "string",
    "message_count": 0,
    "needs_human": false,
    "sender_email": "string",
    "sender_name": "string",
    "severity": "string",
    "status": "string",
    "subject": "string",
    "triage_summary": "string"
  }
]
get/support/threads/{thread_id}

Get Thread

Parameters

  • thread_idpath · required
    string

Responses

  • 200

    Successful Response

    { category: string | null; created_at: string; id: string; last_message_at: string; latest_draft: string | null; message_count: integer; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X GET "https://api.competitorintelligence.co/support/threads/{thread_id}" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "category": "string",
  "created_at": "2026-04-24T12:00:00Z",
  "id": "00000000-0000-0000-0000-000000000000",
  "last_message_at": "2026-04-24T12:00:00Z",
  "latest_draft": "string",
  "message_count": 0,
  "messages": [],
  "needs_human": false,
  "sender_email": "string",
  "sender_name": "string",
  "severity": "string",
  "status": "string",
  "subject": "string",
  "triage_summary": "string"
}
post/support/threads/{thread_id}/close

Close Thread

Parameters

  • thread_idpath · required
    string

Responses

  • 200

    Successful Response

    { category: string | null; created_at: string; id: string; last_message_at: string; latest_draft: string | null; message_count: integer; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/support/threads/{thread_id}/close" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "category": "string",
  "created_at": "2026-04-24T12:00:00Z",
  "id": "00000000-0000-0000-0000-000000000000",
  "last_message_at": "2026-04-24T12:00:00Z",
  "latest_draft": "string",
  "message_count": 0,
  "needs_human": false,
  "sender_email": "string",
  "sender_name": "string",
  "severity": "string",
  "status": "string",
  "subject": "string",
  "triage_summary": "string"
}
post/support/threads/{thread_id}/escalate

Escalate Thread

Parameters

  • thread_idpath · required
    string

Responses

  • 200

    Successful Response

    { category: string | null; created_at: string; id: string; last_message_at: string; latest_draft: string | null; message_count: integer; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/support/threads/{thread_id}/escalate" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response200 · application/json
{
  "category": "string",
  "created_at": "2026-04-24T12:00:00Z",
  "id": "00000000-0000-0000-0000-000000000000",
  "last_message_at": "2026-04-24T12:00:00Z",
  "latest_draft": "string",
  "message_count": 0,
  "needs_human": false,
  "sender_email": "string",
  "sender_name": "string",
  "severity": "string",
  "status": "string",
  "subject": "string",
  "triage_summary": "string"
}
post/support/threads/{thread_id}/reply

Reply To Thread

Parameters

  • thread_idpath · required
    string

Request body · required

application/json{ text: string | null }

Responses

  • 200

    Successful Response

    { category: string | null; created_at: string; id: string; last_message_at: string; latest_draft: string | null; message_count: integer; … }
  • 422

    Validation Error

    { detail: array<ValidationError> }

Try it

curl -X POST "https://api.competitorintelligence.co/support/threads/{thread_id}/reply" \
  -H "Authorization: Bearer $CI_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "string"
}'
Example response200 · application/json
{
  "category": "string",
  "created_at": "2026-04-24T12:00:00Z",
  "id": "00000000-0000-0000-0000-000000000000",
  "last_message_at": "2026-04-24T12:00:00Z",
  "latest_draft": "string",
  "message_count": 0,
  "needs_human": false,
  "sender_email": "string",
  "sender_name": "string",
  "severity": "string",
  "status": "string",
  "subject": "string",
  "triage_summary": "string"
}
post/support/webhooks/agentmail

Agentmail Webhook

Receive inbound mail events from AgentMail. AgentMail posts JSON like:: {"event_type": "message.received", "inbox_id": "...", "message": {...}} Auth: when ``AGENTMAIL_WEBHOOK_SECRET`` is set we verify the Svix-style signature AgentMail sends (``svix-id`` / ``svix-timestamp`` / ``svix-signature`` headers, HMAC-SHA256 of ``"{id}.{ts}.{body}"``). If no secret is configured we accept anything — useful for first-time setup but you should set the secret in production.

Responses

  • 202

    Successful Response

    any

Try it

curl -X POST "https://api.competitorintelligence.co/support/webhooks/agentmail" \
  -H "Authorization: Bearer $CI_API_TOKEN"
Example response202 · application/json
"string"