Everything the console shows, from your own tools: sites, runs, every change, keywords with demand and position, AI visibility, and the approval queue. Webhooks tell you the moment something happens, signed so you can trust them.
Create a token under Settings › API and webhooks. Send it as a bearer token. Tokens are stored hashed and can be revoked at any time.
curl https://app.seenoria.com/api/v1/me \
-H "Authorization: Bearer sk_..."
Limits: 120 requests a minute per token. All timestamps are ISO 8601 in UTC.
| Method and path | Returns |
|---|---|
GET /api/v1/me | Account name, plan, number of sites, token name. |
GET /api/v1/sites | Every site: slug, name, domain, CMS, status, mode (dry_run or live), last run time. |
GET /api/v1/sites/{slug} | One site with its last run, 28-day clicks, impressions, sessions, AI answers checked and cited, connection health, approvals waiting, and the client report link. |
GET /api/v1/sites/{slug}/runs?limit=30 | Nightly runs, newest first: status, one-line summary, cost. |
GET /api/v1/sites/{slug}/changes?from=2026-09-01&to=2026-09-30&limit=100 | Every change with before and after values, who did it and why. Kinds: applied, reverted. |
GET /api/v1/sites/{slug}/keywords | Tracked keywords with priority, target page, monthly searches, CPC, competition, latest Google position and why the agent tracks it. |
GET /api/v1/sites/{slug}/ai-visibility | The latest night's AI answers: each buyer question, platform, whether the site was named, at which position, and which competitors were. |
GET /api/v1/sites/{slug}/approvals | Changes waiting for a decision, with the proposed before and after and the reason. |
POST /api/v1/sites/{slug}/approvals/{id} | Body {"decision": "approve" | "reject", "note": "optional"}. Approved changes apply on the next nightly run. |
Responses wrap the result in data. Errors return a JSON error with the HTTP status: 401 for a bad token, 404 for a site or approval that is not yours, 422 for a bad body, 429 when you exceed the limit.
Add an HTTPS URL under Settings › API and webhooks and pick the events, or leave all unticked for everything. Each delivery is a JSON POST:
{
"id": 512,
"event": "approval.needed",
"site_id": 3,
"occurred_at": "2026-10-01T23:04:11+00:00",
"data": { "action_id": 981, "site": "harbour", "type": "blog_post",
"target": "https://harbour-accountants.com/blog/vat-deadlines",
"rationale": "...", "url": "https://app.seenoria.com/approvals" }
}
| Event | When |
|---|---|
run.finished | A nightly run finished |
approval.needed | A change is waiting for a decision |
change.applied | A change was applied on the site |
change.reverted | A change was reverted |
report.monthly | The monthly client report was sent |
ping | The "Send test" button. |
Every request carries X-Seenoria-Signature: sha256=<hex>, the HMAC-SHA256 of the raw body with the webhook's secret, plus X-Seenoria-Event and X-Seenoria-Delivery. Compute the HMAC over the exact bytes you received and compare with a constant-time function. Answer with any 2xx within ten seconds; anything else is retried after 1, 5, 30, 120 and 720 minutes, then marked failed. The console shows the last result next to each webhook.
# Python
import hmac, hashlib
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, request.headers["X-Seenoria-Signature"])