Skip to main content

Overview

Monitors turn entity interests into standing alerts. A tracker watches one entity; a monitor groups trackers and owns delivery settings (email, Slack, webhook).
  • Base URL: https://api.arcmira.com, all paths under /v1.
  • Auth: Authorization: Bearer arc_sk_... or x-api-key: arc_sk_....
  • Scopes: read for all GETs; monitors:write for non-GET /v1/monitors*; trackers:write for non-GET /v1/trackers*.
  • Send Idempotency-Key (a UUID) on every POST/PATCH; replays return the cached response with Idempotency-Replayed: true.
  • Tier gates: Slack delivery from Free; webhook delivery and realtime email on paid tiers.
  • Billing: monitor and tracker CRUD is free. Each alert occurrence (a tracker matching a new appearance) debits 100 rows from the shared monthly pool, once per occurrence no matter how many channels it fans out to.
  • Alerts fire for recently published media (roughly a 14-day publish window) as it is analyzed. This is monitoring, not retroactive search.

Minimal working example

Endpoints

Monitors

Trackers

Request parameters

Monitor create/update body (all fields optional except name on create): Tracker create/update body (POST /v1/trackers; creating the same entity twice returns 409 with the existingId):

Response schema

Monitor object (create/read):
Alert history row (GET /v1/monitors/{id}/alerts?n=25; also per tracker):

Webhooks

Webhook delivery POSTs each alert to your webhookUrl as JSON. Paid tiers only. Endpoint requirements: HTTPS, publicly reachable, responds 2xx within 10 seconds.

Delivery payload

Request headers

Every delivery carries:

Signature verification

The signature is HMAC-SHA256 over the string {X-Arcmira-Timestamp}.{raw request body}, keyed by your webhook secret (whsec_..., with the whsec_ prefix stripped before use), base64-encoded, and sent as sha256=<base64>. Reject deliveries whose timestamp is more than 300 seconds from now (replay protection), and always compare in constant time.
Verify against the raw request body bytes, before any JSON parsing or re-serialization. Framework body parsers that re-encode JSON will break the signature.

Secret lifecycle

  • The signing secret (whsec_...) is generated when webhook delivery is first enabled on a monitor.
  • The secret is returned exactly once, in the response that enabled the webhook. Store it securely; reads return webhookSecretSet: true and a hint only.
  • Lost it? Rotate: POST /v1/monitors/{id}/webhook-secret/rotate (scope monitors:write) returns a fresh secret once. During a 24h overlap window, deliveries are signed with both secrets (X-Arcmira-Signature new, X-Arcmira-Signature-Previous old) so you can roll with zero downtime.
  • Rotation does not re-enable a disabled webhook; re-enable explicitly with PATCH { "notifyWebhook": true } after fixing your endpoint.

Retries and auto-disable

The delivery queue retries failed deliveries (platform-default schedule; rate-limited endpoints retry after 60 seconds). 10 consecutive failures auto-disable the webhook: notifyWebhook stays configured but webhookDisabledAt/webhookDisabledReason are set and deliveries stop. Auto-disable is enforced per tracker today; a monitor-level webhook’s failure counter accrues on the tracker that fired (fix pending), so also watch your endpoint’s own logs.

Errors

Envelope: { "error": { "type", "code", "message", "doc_url", "request_id" } }. Switch on error.code. Documented exception: tier gates on delivery features (webhook or Slack requested on a plan without them) return a legacy bare 403 body, { "error": "...", "upgradeRequired": true, "feature": "webhooks" }, without the envelope. Detect it by the upgradeRequired field.

Rate limits & idempotency

  • Per-key sliding 60s window (free 60/min, paid 240, teams/enterprise 600); RateLimit-* headers on every response.
  • Idempotency-Key on every POST/PATCH; 24h cache per key; replays return Idempotency-Replayed: true; reuse with a different body returns 409.

Community Review

Any fired alert is disputable, free (0 rows), via type: "monitor_alert" on POST /v1/feedback. Corrections key off the alert row id; referenced rows must belong to your account (404 alert_not_found otherwise):
Issue set: false_positive_alert, wrong_entity, wrong_media, wrong_timestamp, duplicate_alert, missed_alert (expectation shape: no row to target; suggested_change carries source_url + approximate_timestamp_seconds), delivery_issue (targets the delivery row; suggested_change: { "channel": ... }), other. Content disputes target the occurrence, not each delivery row; file once per appearance_id. Check status later with GET /v1/feedback/{feedback_id}. Nothing auto-applies. Catalog: Community Review.

Common mistakes

Patterns and gotchas

  • Webhook for speed, polling for reconciliation: consume webhooks in realtime and sweep ?n=100 alert history periodically to catch anything missed during downtime.
  • Respond 2xx fast (under 10s) and process async; slow endpoints accumulate failures and auto-disable after 10 consecutive.
  • To stop deliveries, pause the tracker: PATCH /v1/trackers/{id} with { "paused": true }. There is no backfill for the paused window. Monitor-level isPaused is dashboard state and does not stop tracker deliveries today.
  • Alert occurrences debit 100 rows each; /v1/meusage.rows_remaining is the live balance.
  • Deliveries from usage-cap pauses appear as missed alerts in the dashboard and can be replayed there.
  • Dashboard-created monitors and API-created monitors are the same objects; you can mix freely.

Complete examples

Create, deliver, verify, and reconcile:
TypeScript