Pre-Trip API

Scan from your own tools.

One keyed endpoint runs the same engine as the web scanner: paste, a live URL, or a creative image in, a source-cited pass or fix list back. Your draft is scanned in memory and never stored.

Base URL and authentication

All requests go to:

https://scan.kesey.agency/api/v1

Authenticate with an API key as a bearer token. Create and revoke keys in your account under API keys. The key is shown once at creation; store it somewhere safe.

Authorization: Bearer pt_live_xxxxxxxxxxxxxxxx

A key spends the owning account's Bus Pass or token balance. The free trial is web-only, so a keyed scan resolves pass then tokens; out of both returns 402.

Scan a draft

POST/api/v1/scan

Send the facets that resolve which rulesets apply, plus the draft itself in one of three ways: pasted text, a URL we fetch, or an image we transcribe.

Request fields

FieldTypeNotes
sourcestringOne of paste, url, image. Defaults to paste.
asset_typestringOne of email, landing_page, blog, social.
statestringTwo-letter state code marketed into, e.g. CA.
verticalstringThe regulated vertical. See /api/library for the current vocabulary.
covered_entitybooleanTrue if the advertiser is a HIPAA covered entity.
subjectstringEmail subject and preview text. Email only, optional.
bodystringThe draft copy, for source: paste. HTML or plain text, up to 60,000 characters.
urlstringThe page to fetch and scan, for source: url.
imageobjectFor source: image: { media_type, data_b64 }. Types: png, jpeg, webp, gif.

Example

curl https://scan.kesey.agency/api/v1/scan \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "paste",
    "asset_type": "email",
    "state": "CA",
    "vertical": "addiction_treatment",
    "covered_entity": false,
    "subject": "Beat addiction this month",
    "body": "Our program guarantees a full recovery. Call now."
  }'

Response

A JSON object with four parts:

  • scan: the deterministic verdict, findings (each with the quoted line, the rule, and the fix), cleared rulesets, and any standing checks.
  • ai: the advisory pass. It can only add nuance, never clear a finding.
  • intake: the mode, and for url or image the exact text that was scanned, echoed back so the basis is never a black box.
  • spend: which entitlement was spent (pass or token) and what remains.

Rate limits

Requests are limited per key, by default 60 per minute. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining. Over the limit returns 429 with a Retry-After header in seconds.

Errors

StatuserrorMeaning
400bad requestA field is missing or invalid. The message names it.
401unauthorizedThe key is missing, malformed, or revoked.
402out_of_scansNo pass or tokens left. Buy more in your account.
422extract failedA URL would not load or an image was unreadable. Nothing was spent.
429rate_limitedOver the per-key rate limit. Retry after the given seconds.

A scan that fails after spending refunds itself, so a 500 never costs a scan.

What happens to your draft

Your draft is processed in memory for the check and never stored: not in a database, not in logs, not in analytics. The only thing recorded per scan is aggregate metadata, the verdict shape and rule ids, tied to your key for your own usage view. Never the copy itself. How we handle data.

Machine-readable spec

An OpenAPI description of this endpoint is at /developers/openapi.json.