# BytePilot > White-label AI services platform for agencies and resellers. Credit-based > billing (GBP pence), named API keys with scopes and per-key spend caps. There are TWO APIs, on two hosts, sharing ONE key: - Platform API — https://bytepilot.ai/api/v1 — Read and control your account. - Model API — https://models.bytepilot.ai/v1 — Call AI models directly, on your account. Auth: X-API-Key header (or Authorization: Bearer) with a business API key from your dashboard Developers page. Keys carry scopes and optional per-key daily/monthly spend caps (HTTP 402 when hit). Scopes: - agents: Agents & clients - calls: Calls & transcripts - billing: Balance, ledger & usage reports - models: Model API (the OpenAI-compatible endpoint) - outbound: Outbound calls & schedules - visibility: AI visibility checks & reports - leads: Lead intelligence campaigns & prospects ## Docs - Full endpoint reference: https://bytepilot.ai/llms-full.txt - OpenAPI spec: https://bytepilot.ai/openapi.json - Human docs: https://bytepilot.ai/developers ## Platform API (https://bytepilot.ai/api/v1) - GET https://bytepilot.ai/api/v1/agents — List your agents [scope: agents] - POST https://bytepilot.ai/api/v1/agents/:id/status — Pause or resume an agent [scope: agents] - GET https://bytepilot.ai/api/v1/clients — List your clients [scope: agents] - POST https://bytepilot.ai/api/v1/clients — Create a client (idempotent by name) [scope: agents] - GET https://bytepilot.ai/api/v1/visibility/targets — List tracked websites [scope: visibility] - POST https://bytepilot.ai/api/v1/visibility/targets — Track a website (idempotent by domain) [scope: visibility] - POST https://bytepilot.ai/api/v1/visibility/targets/:id/run — Run a visibility check [scope: visibility] - GET https://bytepilot.ai/api/v1/visibility/targets/:id/reports — List a website's reports [scope: visibility] - GET https://bytepilot.ai/api/v1/visibility/reports/:id — Get a report [scope: visibility] - GET https://bytepilot.ai/api/v1/visibility/reports/:id/pdf — Download a report as PDF [scope: visibility] - GET https://bytepilot.ai/api/v1/leads/campaigns — List lead campaigns [scope: leads] - POST https://bytepilot.ai/api/v1/leads/campaigns — Start a lead campaign [scope: leads] - GET https://bytepilot.ai/api/v1/leads/campaigns/:id — Get a campaign [scope: leads] - GET https://bytepilot.ai/api/v1/leads/campaigns/:id/prospects — List a campaign's scored prospects [scope: leads] - GET https://bytepilot.ai/api/v1/leads/prospects/:id — Get a prospect in full [scope: leads] - GET https://bytepilot.ai/api/v1/leads/campaigns/:id/pdf — Download a campaign report as PDF [scope: leads] - GET https://bytepilot.ai/api/v1/calls — List calls in a date range [scope: calls] - GET https://bytepilot.ai/api/v1/calls/:id — One call with transcript and message [scope: calls] - GET https://bytepilot.ai/api/v1/balance — Current credit balance [scope: billing] - GET https://bytepilot.ai/api/v1/ledger — Credit ledger entries in a date range [scope: billing] - GET https://bytepilot.ai/api/v1/usage — Usage report: units + cost per client, agent and channel [scope: billing] - POST https://bytepilot.ai/api/v1/outbound/calls — Ring someone now [scope: outbound] - GET https://bytepilot.ai/api/v1/outbound/calls — Outbound call history, including refused ones [scope: outbound] - POST https://bytepilot.ai/api/v1/outbound/schedules — Schedule recurring calls [scope: outbound] - GET https://bytepilot.ai/api/v1/outbound/schedules — List your schedules [scope: outbound] - POST https://bytepilot.ai/api/v1/outbound/schedules/:id/cancel — Stop a schedule [scope: outbound] - POST https://bytepilot.ai/api/v1/outbound/do-not-call — Add a number to the do-not-call list [scope: outbound] - GET https://bytepilot.ai/api/v1/outbound/do-not-call — List do-not-call entries [scope: outbound] ## Model API (https://models.bytepilot.ai/v1) - POST https://models.bytepilot.ai/v1/chat/completions — Create a chat completion [scope: models] - GET https://models.bytepilot.ai/v1/models — List the models you can call [scope: models] --- # Platform API Your agents, clients, calls, transcripts, credit balance and usage reports — the data behind the dashboard, in JSON. This is what you automate against: pull last month's usage per client and raise your own invoices, sync clients from your CRM, pause an agent from your own admin panel. Base URL: https://bytepilot.ai/api/v1 (Every endpoint below is this prefix plus its own path.) Auth: Send a business API key as X-API-Key (or Authorization: Bearer). Create keys on your dashboard's Developers page. Pull this month's usage, rolled up per client: ```bash curl https://bytepilot.ai/api/v1/usage \ -H 'X-API-Key: bp_live_your_key_here' ``` # Model API A drop-in replacement for the OpenAI API. Change two lines in any existing OpenAI-compatible app — the base URL and the key — and it runs on your BytePilot balance instead. Same request and response shapes, streaming included; we forward your request untouched and hand back exactly what came out. It exists so the technical end of your book has somewhere to go. A client who wants to build their own thing gets a key from you, spends against your balance, and appears as a line on your usage report — so you can bill them for it at whatever margin you set. Give each client their own key, cap it, and read its spend separately. Billing is a multiple of what the request actually costs us, not a per-token rate — model prices move monthly and a fixed rate would be wrong within weeks. Fractions of a penny accumulate rather than rounding up, so a thousand tiny requests cost what a thousand tiny requests should. Base URL: https://models.bytepilot.ai/v1 (Include the /v1 — this is the exact string that goes in an OpenAI client's base_url, and the client appends /chat/completions itself.) Auth: The SAME business API key, with the models scope. Send it as Authorization: Bearer (what OpenAI clients do by default) or X-API-Key. Any OpenAI client — point it at us and go: ```python from openai import OpenAI client = OpenAI( base_url="https://models.bytepilot.ai/v1", api_key="bp_live_your_key_here", ) reply = client.chat.completions.create( model="google/gemini-3.1-flash-lite", messages=[{"role": "user", "content": "Summarise this enquiry in one line."}], ) print(reply.choices[0].message.content) ``` Or straight over HTTP: ```bash curl https://models.bytepilot.ai/v1/chat/completions \ -H 'Authorization: Bearer bp_live_your_key_here' \ -H 'Content-Type: application/json' \ -d '{ "model": "google/gemini-3.1-flash-lite", "messages": [{"role": "user", "content": "Hello"}] }' ``` # Errors Both APIs use the same codes. - 401: The key is missing, malformed or revoked. - 403: The key is valid but lacks the scope this endpoint needs. - 402: Out of credit, or this key hit its own daily/monthly spend cap. Top up, or raise the cap on the Developers page. - 404: No such record — or it belongs to another account. We never distinguish the two. - 429: Too many requests. Back off and retry. --- # Platform API — endpoints ## GET https://bytepilot.ai/api/v1/agents List your agents. Every agent on your account with its status, template, assigned phone number, and client grouping (external_ref round-trips your own CRM/accounting ids). Auth: Business API key: X-API-Key header or Authorization: Bearer. Requires the agents scope. Required scope: agents Response example (200): ```json { "agents": [ { "agent_id": 12, "name": "Sophie", "template": "generic_reception", "status": "ACTIVE", "phone_number": "+441256222333", "client": { "client_id": 3, "name": "Harrison & Co", "external_ref": "CRM-1042" }, "created_at": "2026-07-28 09:00:00" } ] } ``` Status codes: 200 = Agent list; 401 = Invalid key; 403 = Missing scope; 402 = Key spend cap reached --- ## POST https://bytepilot.ai/api/v1/agents/:id/status Pause or resume an agent. Paused agents answer with a polite unavailable message. The change applies from the next call. Auth: Business API key with the agents scope. Required scope: agents Parameters: - id (path, integer, required): Agent id Request example: ```json { "status": "PAUSED" } ``` Response example (200): ```json { "agent_id": 12, "status": "PAUSED" } ``` Status codes: 200 = Updated; 400 = Bad status; 404 = Unknown agent; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/clients List your clients. The client groupings your agents roll up to for billing. Auth: Business API key with the agents scope. Required scope: agents Response example (200): ```json { "clients": [ { "client_id": 3, "name": "Harrison & Co", "external_ref": "CRM-1042", "created_at": "2026-07-01 10:00:00" } ] } ``` Status codes: 200 = Client list; 401 = Invalid key; 403 = Missing scope --- ## POST https://bytepilot.ai/api/v1/clients Create a client (idempotent by name). Creates a client grouping, or returns the existing one with that name (updating external_ref if supplied) — safe to call from sync jobs. Auth: Business API key with the agents scope. Required scope: agents Request example: ```json { "name": "Harrison & Co", "external_ref": "CRM-1042" } ``` Response example (200): ```json { "client_id": 3, "name": "Harrison & Co", "external_ref": "CRM-1042", "created": true } ``` Status codes: 200 = Created or matched; 400 = Missing name; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/visibility/targets List tracked websites. Every website you track for AI visibility, with its latest score and the client it belongs to. AI visibility measures how often leading AI assistants (ChatGPT, Claude, Gemini, Perplexity) recommend a business when people ask the buying questions its customers actually ask. Auth: Business API key: X-API-Key header or Authorization: Bearer. Requires the visibility scope. Required scope: visibility Response example (200): ```json { "targets": [ { "target_id": 5, "url": "https://millerplumbing.co.uk", "domain": "millerplumbing.co.uk", "display_name": "Miller Plumbing", "category": "emergency plumber", "location": "Leeds", "client": { "client_id": 3, "name": "Miller Plumbing", "external_ref": "CRM-1042" }, "latest_report_id": 41, "latest_score": 43, "created_at": "2026-08-01 09:00:00" } ] } ``` Status codes: 200 = Target list; 401 = Invalid key; 403 = Missing scope --- ## POST https://bytepilot.ai/api/v1/visibility/targets Track a website (idempotent by domain). Reads the website once to derive the business name, category, location and the set of buyer-style questions every report will measure against — so scores stay comparable over time. Returns the existing target if the domain is already tracked. Creating a target is free; running reports costs credit. Auth: Business API key with the visibility scope. Required scope: visibility Request example: ```json { "url": "https://millerplumbing.co.uk", "client_name": "Miller Plumbing" } ``` Response example (200): ```json { "target_id": 5, "domain": "millerplumbing.co.uk", "display_name": "Miller Plumbing", "category": "emergency plumber", "location": "Leeds", "created": true } ``` Status codes: 200 = Created or matched; 400 = Missing url; 422 = Website unreadable; 503 = Feature unavailable; 401 = Invalid key; 403 = Missing scope --- ## POST https://bytepilot.ai/api/v1/visibility/targets/:id/run Run a visibility check. Queues a full report: every measured assistant is asked every buyer question, the website gets technical AI-readiness checks, and recommendations are drafted. Reports take a few minutes and you are only charged (flat rate per report, shown as price_pence) when one completes — a failed run is free. Optional send_to / copy_to email the finished report automatically under your branding with the PDF attached, exactly like the dashboard send. Auth: Business API key with the visibility scope. Required scope: visibility Parameters: - id (path, integer, required): Target id Request example: ```json { "send_to": "owner@millerplumbing.co.uk", "copy_to": "you@youragency.co.uk" } ``` Response example (200): ```json { "report_id": 42, "status": "QUEUED", "price_pence": 200, "note": "Reports take a few minutes. Poll GET /api/v1/visibility/reports/{report_id}. You are only charged when it completes." } ``` Status codes: 200 = Queued; 402 = Balance below the report price; 404 = Unknown target; 409 = A check is already running; 429 = Daily check limit reached; 503 = Feature unavailable; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/visibility/targets/:id/reports List a website's reports. Report history for one tracked website, newest first (up to 50) — the score trend an agency charts for its client. Auth: Business API key with the visibility scope. Required scope: visibility Parameters: - id (path, integer, required): Target id Response example (200): ```json { "reports": [ { "report_id": 41, "status": "COMPLETE", "score": 43, "charged_pence": 200, "scheduled": false, "created_at": "2026-08-18 08:00:00", "completed_at": "2026-08-18 08:07:00" } ] } ``` Status codes: 200 = Report list; 404 = Unknown target; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/visibility/reports/:id Get a report. The full report: overall 0–100 score, per-assistant breakdown, who the assistants recommend instead, the website's AI-readiness checks and the drafted recommendations. Completed reports also include agent_prompt — a ready-to-paste brief for an AI coding agent working on the measured website, containing the baseline, the buyer questions being lost and a prioritised task list; hand it to your own tooling or to the client's developer. Pass include_answers=1 for the question-by-question evidence. Poll this after queuing a run — status moves QUEUED → RUNNING → COMPLETE (or FAILED, uncharged). Auth: Business API key with the visibility scope. Required scope: visibility Parameters: - id (path, integer, required): Report id - include_answers (query, boolean): Set 1 to include every assistant answer excerpt Response example (200): ```json { "report_id": 41, "target_id": 5, "status": "COMPLETE", "score": 43, "assistants": [ { "assistant": "ChatGPT", "available": true, "questions_answered": 10, "times_recommended": 4, "score": 40 } ], "competitors": [ { "name": "Aqua Flow Plumbers", "mentions": 5 } ], "site_checks": { "schema_org": false, "llms_txt": false, "blocked_crawlers": [ "GPTBot" ] }, "recommendations": { "summary": "Strong on emergency questions, invisible on boiler servicing.", "gaps": [ "Blocked GPTBot means ChatGPT cannot read the site" ], "actions": [ "Unblock GPTBot in robots.txt" ] }, "agent_prompt": "You are working on the website for Miller Plumbing (https://millerplumbing.co.uk)\u2026\nTASKS, in priority order:\n1. robots.txt currently blocks GPTBot\u2026", "charged_pence": 200, "scheduled": false, "created_at": "2026-08-18 08:00:00", "completed_at": "2026-08-18 08:07:00" } ``` Status codes: 200 = The report; 404 = Unknown report; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/visibility/reports/:id/pdf Download a report as PDF. The client-ready PDF, carrying YOUR branding (name, logo, colour from your dashboard branding page) and nothing of ours — the same document the dashboard sends to clients. Binary application/pdf response. Auth: Business API key with the visibility scope. Required scope: visibility Parameters: - id (path, integer, required): Report id (must be COMPLETE) Response example (200): ```json { "note": "Binary PDF body, Content-Type: application/pdf" } ``` Status codes: 200 = PDF document; 404 = Unknown or unfinished report; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/leads/campaigns List lead campaigns. Your lead-intelligence campaigns, newest first. A campaign finds businesses matching an industry and location, researches each one's website and web presence, scores how strong a prospect it is for the services you sell, and drafts the outreach — you are billed a flat rate per prospect scored, never for discovery or failed research. Auth: Business API key: X-API-Key header or Authorization: Bearer. Requires the leads scope. Required scope: leads Response example (200): ```json { "campaigns": [ { "campaign_id": 7, "label": "Dentists in Manchester", "industry": "dentists", "location": "Manchester", "services": [ "Websites", "AI receptionist" ], "status": "COMPLETE", "target": 25, "discovered": 25, "scored": 23, "failed": 2, "created_at": "2026-08-27 09:00:00", "completed_at": "2026-08-27 09:41:00" } ] } ``` Status codes: 200 = Campaign list; 401 = Invalid key; 403 = Missing scope --- ## POST https://bytepilot.ai/api/v1/leads/campaigns Start a lead campaign. Queues discovery and research. Your balance must cover the campaign's maximum cost (count × per-prospect rate) up front, but charges land per prospect actually scored. Optional send_to / copy_to email the finished white-label prospect report (your branding, PDF attached) when the campaign completes. exclude_previous (default true) skips businesses already found for the SAME client (campaigns with no client form their own scope) — so a monthly re-run stays fresh, while the same lead can still surface for a different client it also suits. Optional seller_context (a sentence or two on what the seller is and offers) tailors the scoring, pitches and drafts to what is genuinely on offer — the dashboard derives it automatically from the seller's website. Better still, pass profile_id (a seller profile created in the dashboard): the campaign then also carries the seller's confirmed proof points, ideal customer, sign-off name and booking link, and drafts come out ready to send. Profiles are read fresh at scoring time, so profile edits apply to every later run. Auth: Business API key with the leads scope. Required scope: leads Request example: ```json { "industry": "dentists", "location": "Manchester", "count": 25, "services": [ "Websites", "AI receptionist" ], "client_name": "Smile Group", "seller_context": "Smile Group: a dental marketing agency offering websites and AI reception for practices.", "send_to": "owner@smilegroup.co.uk", "copy_to": "you@youragency.co.uk" } ``` Response example (200): ```json { "campaign_id": 7, "status": "QUEUED", "price_per_prospect_pence": 25, "max_cost_pence": 625, "note": "Campaigns take several minutes. Poll GET /api/v1/leads/campaigns/{campaign_id}. You are only charged per prospect scored." } ``` Status codes: 200 = Queued; 400 = Missing industry, location or services; 402 = Balance below the campaign ceiling; 429 = Daily research limit reached; 503 = Feature unavailable; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/leads/campaigns/:id Get a campaign. Progress and totals for one campaign. Poll this after starting one — status moves QUEUED → DISCOVERING → RESEARCHING → COMPLETE (or FAILED, uncharged). Auth: Business API key with the leads scope. Required scope: leads Parameters: - id (path, integer, required): Campaign id Response example (200): ```json { "campaign_id": 7, "label": "Dentists in Manchester", "industry": "dentists", "location": "Manchester", "services": [ "Websites", "AI receptionist" ], "status": "RESEARCHING", "target": 25, "discovered": 25, "scored": 11, "failed": 1, "charged_pence": 275, "created_at": "2026-08-27 09:00:00", "completed_at": null } ``` Status codes: 200 = The campaign; 404 = Unknown campaign; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/leads/campaigns/:id/prospects List a campaign's scored prospects. Scored prospects, best first, with per-service fit scores and the recommended pitch. Filter with min_score to feed only strong prospects into your own tooling. Auth: Business API key with the leads scope. Required scope: leads Parameters: - id (path, integer, required): Campaign id - min_score (query, integer): Only prospects scoring at least this (0-100) Response example (200): ```json { "prospects": [ { "prospect_id": 91, "name": "Smile Dental", "website": "https://smiledental.example", "domain": "smiledental.example", "phone": "0161 000 0000", "lead_score": 91, "fit_scores": { "Websites": 41, "AI receptionist": 96 }, "pitch": "Lead with the AI receptionist \u2014 no out-of-hours cover and no online booking." } ] } ``` Status codes: 200 = Prospect list; 404 = Unknown campaign; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/leads/prospects/:id Get a prospect in full. Everything the research produced: the observed website signals (deterministic), best-effort external notes, per-service fit scores, the personalised angle, and the outreach drafts (cold email and call script) ready to send from YOUR OWN tools — the platform never sends outreach itself. Auth: Business API key with the leads scope. Required scope: leads Parameters: - id (path, integer, required): Prospect id Response example (200): ```json { "prospect_id": 91, "campaign_id": 7, "name": "Smile Dental", "website": "https://smiledental.example", "domain": "smiledental.example", "contact_email": "hello@smiledental.example", "phone": "0161 000 0000", "status": "SCORED", "lead_score": 91, "fit_scores": { "Websites": 41, "AI receptionist": 96 }, "signals": [ { "signal": "Online booking", "level": "warn", "detail": "No visible way to book online \u2014 customers must call during opening hours." } ], "external_notes": { "reviews": "around 23 Google reviews", "competitors": [ "Brighter Smiles" ] }, "pitch": "Lead with the AI receptionist.", "angle": "Their site asks patients to phone for appointments, with no way to book or ask questions outside opening hours.", "email_subject": "Out-of-hours enquiries at Smile Dental", "email_draft": "Hi, I was looking at smiledental.example and noticed\u2026", "call_script": "OPENER: \u2026", "charged_pence": 25 } ``` Status codes: 200 = The prospect; 404 = Unknown prospect; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/leads/campaigns/:id/pdf Download a campaign report as PDF. The client-ready prospect report, carrying YOUR branding and nothing of ours — the same document the dashboard emails to clients. Binary application/pdf response. Auth: Business API key with the leads scope. Required scope: leads Parameters: - id (path, integer, required): Campaign id (must be COMPLETE) Response example (200): ```json { "note": "Binary PDF body, Content-Type: application/pdf" } ``` Status codes: 200 = PDF document; 404 = Unknown or unfinished campaign; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/calls List calls in a date range. Filterable by agent_id and client_id; defaults to the current month. Latest 200. Auth: Business API key with the calls scope. Required scope: calls Parameters: - from (query, string): YYYY-MM-DD (default: first of this month) - to (query, string): YYYY-MM-DD (default: today) - agent_id (query, integer): Filter to one agent - client_id (query, integer): Filter to one client Response example (200): ```json { "from": "2026-07-01", "to": "2026-07-28", "calls": [ { "call_id": 88, "agent_id": 12, "channel_id": 15, "client_id": 3, "caller_number": "+447712345678", "started_at": "2026-07-28 14:03:11", "duration_seconds": 151, "status": "COMPLETED", "outcome": "message_taken", "summary": "Quote request", "charged_pence": 26 } ] } ``` Status codes: 200 = Call list; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/calls/:id One call with transcript and message. The full record: timings, outcome, the structured message taken (if any) and the conversation transcript (subject to your transcript retention window). Auth: Business API key with the calls scope. Required scope: calls Parameters: - id (path, integer, required): Call id Response example (200): ```json { "call_id": 88, "agent_id": 12, "channel_id": 15, "client_id": 3, "caller_number": "+447712345678", "to_number": "+441256222333", "started_at": "2026-07-28 14:03:11", "ended_at": "2026-07-28 14:05:42", "duration_seconds": 151, "status": "COMPLETED", "outcome": "message_taken", "summary": "Quote request", "message": { "caller_name": "John Peters", "reason": "Quote request", "details": "Ltd company, ~40 invoices/month" }, "transcript": [ "[0:01] Agent: Hi, this is Sophie\u2026" ], "charged_pence": 26, "rate_ppu_used": 10 } ``` Status codes: 200 = Call detail; 404 = Unknown call; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/balance Current credit balance. Balance in pence, billing mode, and account status. Auth: Business API key with the billing scope. Required scope: billing Response example (200): ```json { "balance_pence": 892, "billing_mode": "CREDITS", "account_status": "ACTIVE" } ``` Status codes: 200 = Balance; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/ledger Credit ledger entries in a date range. Every balance movement: grants, top-ups, usage debits (with the API key that incurred them, where applicable), adjustments. Latest 500 in range. Auth: Business API key with the billing scope. Required scope: billing Parameters: - from (query, string): YYYY-MM-DD (default: first of this month) - to (query, string): YYYY-MM-DD (default: today) Response example (200): ```json { "from": "2026-07-01", "to": "2026-07-28", "entries": [ { "entry_id": 14, "type": "USAGE", "amount_pence": -13, "service": "voice", "reference": "CA3f84\u2026", "balance_after_pence": 892, "api_key_id": null, "created_at": "2026-07-28 10:46:20" } ] } ``` Status codes: 200 = Ledger entries; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/usage Usage report: units + cost per client, agent and channel. The endpoint your billing automation calls monthly to invoice your clients: usage rolled up channel → agent → client with your external_ref on every row, filtered by date range and optionally client_id / agent_id / channel_id. Auth: Business API key with the billing scope. Required scope: billing Parameters: - from (query, string): YYYY-MM-DD (default: first of this month) - to (query, string): YYYY-MM-DD (default: today) - client_id (query, integer): Filter to one client - agent_id (query, integer): Filter to one agent - channel_id (query, integer): Filter to one channel Response example (200): ```json { "from": "2026-07-01", "to": "2026-07-28", "usage": [ { "service": "voice", "client_id": 3, "client_name": "Harrison & Co", "external_ref": "CRM-1042", "agent_id": 12, "agent_name": "Sophie", "channel_id": 15, "calls": 42, "seconds": 6510, "charged_pence": 1110 } ] } ``` Status codes: 200 = Usage rows; 401 = Invalid key; 403 = Missing scope --- ## POST https://bytepilot.ai/api/v1/outbound/calls Ring someone now. Places a call immediately — the endpoint your CRM hits when a customer asks to be called back. The permission that makes the call lawful is recorded from the `consent` object in the same request, so there is no way to schedule a call without saying why you may make it. Every call, from here or the scheduler, passes the same gate at the moment of dialling: do-not-call list, permission on file, calling hours, and the per-number and per-agent limits. A refused call answers **409 with the reason in plain words** — the request was fine, we simply must not make that call — and is still recorded so it shows in your history. Auth: Business API key with the outbound scope. Required scope: outbound Request example: ```json { "agent_id": 12, "to_number": "+447712345678", "reason": "Quote request from the website", "consent": { "basis": "requested", "source": "Website callback form, 29 July", "tps_attested": false } } ``` Response example (200): ```json { "outbound_id": 84, "status": "placed", "reason": "" } ``` Status codes: 200 = Call placed; 409 = Refused, with the reason (do-not-call, no permission, outside hours, limit reached); 400 = Bad number, or missing/invalid consent; 404 = Unknown agent; 502 = The call could not be placed; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/outbound/calls Outbound call history, including refused ones. Every attempt in the range with what became of it. Refused attempts carry `block_reason`, which is the answer to "why didn't it ring them" — the commonest question this feature produces. Auth: Business API key with the outbound scope. Required scope: outbound Parameters: - from (query, string): YYYY-MM-DD (default: first of this month) - to (query, string): YYYY-MM-DD (default: today) Response example (200): ```json { "from": "2026-07-01", "to": "2026-07-29", "calls": [ { "outbound_id": 84, "agent_id": 12, "agent_name": "Ellie", "client_id": 3, "schedule_id": null, "to_number": "+447712345678", "purpose": "callback", "status": "COMPLETED", "block_reason": null, "call_sid": "CA9f2\u2026", "created_at": "2026-07-29 10:14:02" } ] } ``` Status codes: 200 = Attempt history; 401 = Invalid key; 403 = Missing scope --- ## POST https://bytepilot.ai/api/v1/outbound/schedules Schedule recurring calls. Sets up a repeating call — a morning check-in, a weekly follow-up. `time_of_day` is in the RECIPIENT's timezone, so nine in the morning stays nine in the morning when the clocks change. Cadence is once, daily, weekdays or weekly (`day_of_week` 1–7, Monday first). As with an immediate call, the permission is recorded from `consent` in the same request, and re-checked every time the schedule fires rather than trusted from when it was created. Auth: Business API key with the outbound scope. Required scope: outbound Request example: ```json { "agent_id": 12, "to_number": "+447712345678", "label": "Morning check-in with Doris", "cadence": "daily", "time_of_day": "09:00", "timezone": "Europe/London", "consent": { "basis": "requested", "source": "Arranged with her daughter, 12 July" } } ``` Response example (200): ```json { "schedule_id": 7, "next_run_at": "2026-07-30 08:00:00", "status": "ACTIVE" } ``` Status codes: 200 = Scheduled (next_run_at is UTC); 400 = Bad number, or missing/invalid consent; 404 = Unknown agent; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/outbound/schedules List your schedules. Every schedule on the account with its next call time in UTC. Auth: Business API key with the outbound scope. Required scope: outbound Response example (200): ```json { "schedules": [ { "schedule_id": 7, "agent_id": 12, "label": "Morning check-in with Doris", "to_number": "+447712345678", "cadence": "daily", "time_of_day": "09:00", "day_of_week": null, "timezone": "Europe/London", "next_run_at": "2026-07-30 08:00:00", "status": "ACTIVE" } ] } ``` Status codes: 200 = Schedule list; 401 = Invalid key; 403 = Missing scope --- ## POST https://bytepilot.ai/api/v1/outbound/schedules/:id/cancel Stop a schedule. No further calls are placed. Attempts already made stay in your history. Auth: Business API key with the outbound scope. Required scope: outbound Parameters: - id (path, integer, required): Schedule id Response example (200): ```json { "schedule_id": 7, "status": "CANCELLED" } ``` Status codes: 200 = Cancelled; 404 = Unknown schedule; 401 = Invalid key; 403 = Missing scope --- ## POST https://bytepilot.ai/api/v1/outbound/do-not-call Add a number to the do-not-call list. Nothing overrides this: it is checked at the moment of dialling, and recording fresh permission afterwards does not undo it. `scope` is `account` (every client you run — the default) or `client` with a `client_id` (that one business only). Your agents add entries here themselves whenever somebody asks not to be called again, so you rarely need this. Auth: Business API key with the outbound scope. Required scope: outbound Request example: ```json { "phone_number": "+447712345678", "scope": "account", "note": "Asked by email" } ``` Response example (200): ```json { "phone_number": "+447712345678", "scope": "account", "status": "suppressed" } ``` Status codes: 200 = Added; 400 = Bad number; 401 = Invalid key; 403 = Missing scope --- ## GET https://bytepilot.ai/api/v1/outbound/do-not-call List do-not-call entries. Who your agents will never ring, and why each one is on the list. Auth: Business API key with the outbound scope. Required scope: outbound Response example (200): ```json { "entries": [ { "phone_number": "+447712345678", "scope": "client", "client_id": 3, "reason": "asked_on_call", "note": "Asked during a call", "created_at": "2026-07-29 10:22:41" } ] } ``` Status codes: 200 = Entries; 401 = Invalid key; 403 = Missing scope --- # Model API — endpoints ## POST https://models.bytepilot.ai/v1/chat/completions Create a chat completion. The OpenAI `/v1/chat/completions` contract, unchanged: same request body, same response body, same SSE frames when you set `stream: true`. We do not rewrite your prompt, substitute your model or inject anything — an API that surprises you is worse than one that does less. Every request is attributed to the key that made it, so per-client cost reporting comes free, and a key over its spend cap gets a 402 rather than a surprise bill. Auth: Business API key with the models scope: Authorization: Bearer bp_live_… (or X-API-Key). Required scope: models Request example: ```json { "model": "google/gemini-3.1-flash-lite", "messages": [ { "role": "user", "content": "Summarise this enquiry in one line." } ], "stream": false } ``` Response example (200): ```json { "id": "chatcmpl-\u2026", "object": "chat.completion", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Customer wants a quote for year-end accounts." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 24, "completion_tokens": 11, "total_tokens": 35 } } ``` Status codes: 200 = Completion (or an SSE stream when `stream: true`); 401 = Invalid key; 403 = Key lacks the models scope; 402 = Out of credit, or the key hit its spend cap; 502 = Upstream model service unreachable --- ## GET https://models.bytepilot.ai/v1/models List the models you can call. The catalogue available through the Model API, in the same shape OpenAI clients expect — so a model picker built against OpenAI populates itself. Use the `id` from here as the `model` field in a completion. Auth: Business API key with the models scope. Required scope: models Response example (200): ```json { "object": "list", "data": [ { "id": "google/gemini-3.1-flash-lite", "object": "model" } ] } ``` Status codes: 200 = Model list; 401 = Invalid key; 403 = Key lacks the models scope ---