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_...orx-api-key: arc_sk_.... - Scopes:
readfor all GETs;monitors:writefor non-GET/v1/monitors*;trackers:writefor non-GET/v1/trackers*. - Send
Idempotency-Key(a UUID) on every POST/PATCH; replays return the cached response withIdempotency-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 exceptname 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 yourwebhookUrl 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.
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: trueand a hint only. - Lost it? Rotate:
POST /v1/monitors/{id}/webhook-secret/rotate(scopemonitors:write) returns a fresh secret once. During a 24h overlap window, deliveries are signed with both secrets (X-Arcmira-Signaturenew,X-Arcmira-Signature-Previousold) 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-Keyon every POST/PATCH; 24h cache per key; replays returnIdempotency-Replayed: true; reuse with a different body returns 409.
Community Review
Any fired alert is disputable, free (0 rows), viatype: "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):
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=100alert 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-levelisPausedis dashboard state and does not stop tracker deliveries today. - Alert occurrences debit 100 rows each;
/v1/me→usage.rows_remainingis 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