# Operator onboarding and API guide

This guide is the canonical human-readable path from a new operator account to
a safely operated BOT integration. The machine contract remains
[`openapi.yaml`](../openapi.yaml); webhook messages remain
[`asyncapi.yaml`](../asyncapi.yaml). The current
[BOT Integration Terms](https://integration.boringordertracker.com/terms) are
part of this contract and must be read before an integration is created.

> Current rollout status: operator registration and the synthetic sandbox are
> available. Production data is still disabled behind an independent BOT
> operational gate while the mobile consent/upload rollout and remaining legal
> and operational checks are completed. A successful sandbox test or a manual
> profile approval does not by itself enable production data.

## 1. Register an operator account

1. Open `https://integration.boringordertracker.com/register`.
2. Enter the email address belonging to the person responsible for the
   integration. One account belongs to one person; teams and shared logins are
   not supported in v1.
3. Open the one-time verification link sent by BOT. It expires after 20 minutes.
4. Create a passkey with user verification, for example Face ID, Touch ID,
   Windows Hello, a password manager, or a FIDO security key.
5. Save the recovery codes when they are displayed. They are shown once.

The portal never accepts or stores a password. A normal session expires after
20 minutes of inactivity or eight hours absolutely and is rotated while in use.
Certificate requests, webhook changes, production submissions, review decisions,
and account deletion require a recent passkey confirmation.

If a recovery code is used, all existing portal sessions and passkeys are
revoked and a new passkey must be enrolled. Each recovery code is single-use.

## Monitor API usage and rate limits

Every integration detail page contains an API usage dashboard for the last 30
days. It shows total requests, HTTP 429 responses, response volume, hourly quota
utilization, daily traffic, and a breakdown by stable endpoint name. Sandbox
and production remain separate. The admin view exposes the same operational
measurements without exposing payloads, resource identifiers, certificates, or
source IP addresses.

The dashboard is informational. API clients must continue to treat the
`RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After`
response headers as authoritative.

An operator can enable **Alert me at 90%** separately for sandbox and
production. BOT then sends one multipart HTML/plain-text email per hourly quota
class when usage first reaches 90% of that limit. Standard API traffic and the
separate statistics quota are evaluated independently; retries are deduplicated
for the same integration, recipient, environment, quota class, and hour. Portal
administrators may independently subscribe to the same warning from the admin
integration view. Notification emails contain usage totals and a dashboard
link, never API payloads or BOT user data.

## 2. Create an integration profile

After sign-in, choose **Create integration** on the dashboard. One operator
account may own multiple integrations; each is isolated and reviewed separately.

Before the portal may create an integration, the operator must explicitly
accept the currently published version of the
[BOT Integration Terms](https://integration.boringordertracker.com/terms). The
acceptance is versioned; a new terms version requires a new explicit acceptance.
An earlier checkbox or acceptance is never carried forward silently.

The standard integration licence is **non-commercial only**. Advertising,
Google AdSense, sponsorships, affiliate revenue, subscriptions, paid access,
lead generation, data licensing or resale, and every other direct or indirect
form of monetization are commercial use. Commercial use is permitted only after
BOT and the operator have made an individual written agreement covering that
use. A sandbox client, review approval, owner activation, production
certificate, or user consent does not replace that written agreement.

Supply all of the following:

- public name and a permanent, globally unique slug;
- public description and a specific purpose for using shared data;
- legal operator name, two-letter country code, and support email;
- public HTTPS website, privacy policy, terms of service, and order-linking URL;
- the smallest set of requested scopes;
- the planned polling cadence and optional webhook model;
- acceptance of the current BOT Integration Terms and the mandatory BOT
  attribution/app-link rules;
- an optional direct support or donation URL.

Every URL must be publicly reachable over HTTPS on port 443. The slug cannot be
changed later. Public name, purpose, privacy URL, and scopes are consent-relevant:
a material change creates a new profile/consent boundary and never widens an
existing user's grant silently. During the current sandbox rollout, create a
separate integration if the portal refuses a material profile change.

Available v1 scopes are:

- `orders.read` — current allowed order snapshot;
- `orders.history.read` — complete allowed order change history;
- `vehicles.read` — current allowed snapshot of a specifically selected stock
  vehicle;
- `vehicles.history.read` — allowed, deduplicated stock-vehicle history;
- `shipping.read` — minimized vessel, voyage, port, and shipping events for a
  granted order;
- `statistics.orders.read` — fixed, thresholded aggregate generations only;
- `revocations.ack` — deletion acknowledgements.

Creating the profile immediately creates a separate sandbox client, baseline
quota, approved sandbox scopes, and queues an integration-specific synthetic
corpus. A private worker validates and atomically activates exactly 500
synthetic orders plus 50 synthetic existing vehicles; the web request does not
materialize them. No
BOT user, production order, VIN, or production vehicle is copied into it.

## 3. Upload the integration logo

Open the integration detail page and upload a PNG, JPEG, or WebP image. SVG is
not accepted. The source must be no larger than 2 MiB and both dimensions must
be between 256 and 2048 pixels. BOT decodes the image, removes metadata, and
generates safe 256- and 512-pixel renditions in non-executable storage.

The logo is required before a production review can be submitted. Use a mark
you are legally entitled to publish.

## 4. Obtain a sandbox mTLS certificate

The portal and machine API have separate trust boundaries. Browsers authenticate
to `integration.boringordertracker.com` with a passkey. Servers authenticate to
`api.integration.boringordertracker.com` with a BOT-issued client certificate.

Use one of two issuance modes from the **Certificates** section:

1. **CSR, recommended.** Generate and retain the private key on your own server,
   then paste a PEM PKCS#10 CSR. RSA 3072–8192 and EC P-256/P-384 keys are
   accepted. Download the issued public certificate and CA chain.
2. **One-time encrypted P12.** BOT generates the key in private worker memory.
   Save the one-time claim token, then claim the encrypted package and its
   password within 15 minutes. Both are shown once and BOT does not retain an
   unencrypted private key.

Certificate issuance is asynchronous. Refresh the integration page until the
request is ready. Certificates last 90 days. Up to two certificates may overlap
for a 14-day rotation; quotas remain integration-wide. Revoke a compromised or
retired certificate immediately in the portal.

See [mTLS certificates](mtls-certificates.md) for key handling, rotation, and
edge/backend verification details.

## 5. Exercise the synthetic sandbox

Open **Sandbox** from the integration detail page. The fixture set is private to
that integration and contains only resources allowed by its requested scopes:

- active synthetic order and/or stock-vehicle snapshots;
- complete allowed history events;
- a retained revocation tombstone;
- a fixed privacy-thresholded statistics generation;
- initial sync, incremental sync, deletion acknowledgement, and ETag scenarios.

Use the same API base URL in both environments:

`https://api.integration.boringordertracker.com/v1`

The presented client certificate selects `sandbox` or `production`. Never try to
select an environment with a request parameter or header.

Recommended smoke sequence:

1. call `GET /capabilities` and assert `environment` is `sandbox`;
2. generate a ten-digit code on the portal's Sandbox page;
3. redeem it once with `POST /link-codes/redeem`, preserving leading zeroes;
4. call `GET /sync` without a cursor and transactionally apply the full page;
5. follow each `next_cursor` unchanged and test `If-None-Match`/`304`;
6. fetch order/vehicle detail and every history page for the granted scopes;
7. process the tombstone, delete the local individual copy, and acknowledge it
   with a stable `Idempotency-Key`;
8. if requested, read the fixed statistics generation and verify that it
   contains no individual records.

Do not hard-code fixture IDs or ten-digit codes. They are scoped to the
integration and may be regenerated. Tested curl, PHP, Node.js, and Python
examples are linked from the documentation home page.

## 6. Build the public linking and privacy experience

Before review, implement a public staging version of the operator-side flow.
It must explain that the user deliberately shares selected BOT resources and
must include:

- a **My Order** or equivalent page that accepts the ten-digit BOT code as a
  string;
- no request for the user's BOT login, account ID, real order number, or VIN;
- the mandatory BOT attribution text and BOT app link supplied by the portal,
  displayed prominently and readably wherever BOT data is shown;
- working links to the operator's privacy policy, terms, and support contact;
- deletion behavior for resource and connection revocations;
- a clear separation between a linked personal order and aggregate statistics.

If a page, table, list, card, chart, export, notification, or other output mixes
BOT data with data from another source, mark every BOT-derived record or entry
directly at its output point. For an aggregate visualization, identify each
BOT-derived series or result where it is rendered. A generic footer, legal page,
or page-wide credit alone is not sufficient to identify mixed-source data.

The code is not a durable user identifier. Redeeming it yields only a random
connection ID and random integration-specific resource IDs. A different
integration receives different IDs for the same underlying BOT resource.

### Install the standard footer credit

Add the versioned, tracker-free badge to the public footer:

```html
<footer>
  <span data-bot-attribution data-bot-theme="auto"></span>
  <script src="https://integration.boringordertracker.com/assets/bot-data-credit-v1.js" referrerpolicy="no-referrer" defer></script>
</footer>
```

The script sets no cookies, contains no analytics, and renders “Data provided by Boring Order Tracker with user consent”. Keep `referrerpolicy="no-referrer"` so the partner page URL is not sent with the asset request. Use `auto`, `light`, or
`dark`; when no placeholder is present, the badge is inserted directly beside
the script. This site-wide badge does not replace the per-record, per-value,
per-map-point, per-chart-value, and per-export attribution required above for
mixed-source output.

## 7. Request production review

Complete this checklist before selecting **Submit for production review**:

- all profile and policy fields are accurate and publicly reachable;
- the sanitized logo is uploaded;
- only necessary scopes are requested;
- the public linking flow works with synthetic data;
- BOT attribution and the app link are visible on the submitted review page;
- the current Integration Terms version is accepted and the intended business
  model is declared accurately;
- the standard non-commercial licence covers the intended use, or an individual
  written BOT agreement covering commercial use is already in place;
- mixed-source staging output identifies every BOT-derived record, entry,
  series, or result at its output point;
- the polling/webhook concept describes storage, retries, and deletion handling;
- your backend passes the sandbox lifecycle and rate-limit tests;
- your privacy policy describes the requested data, purpose, retention, and
  revocation path.

The submission action requires a fresh passkey confirmation and a public HTTPS
review URL. The integration then enters `submitted`; the current profile is
frozen for review.

BOT manually checks operator identity, purpose, policies, branding, linking,
requested scopes, storage/deletion behavior, security model, and attribution.
BOT also checks the declared business model against the applicable licence or
individual written agreement. BOT may reject the submission with review notes.
Correct the issues and submit again. Approval is limited to the reviewed profile
version and exact scopes.

## 8. Understand approval versus activation

The statuses have distinct meanings:

- `draft` / `sandbox` — profile work and synthetic API access;
- `submitted` — waiting for manual BOT review;
- `rejected` — changes are required before resubmission;
- `approved` — the reviewed production profile/scopes are accepted and a
  production certificate or production webhook may be prepared;
- `active` — after BOT approval, the integration owner has explicitly pressed
  **Activate for production** for that exact reviewed profile;
- `suspended`, `retired` — production access is stopped.

`approved` is not the same as `active`. Production API authentication requires
the integration to be active. Even then, the independent
`production_data_enabled` capability must be true, and only resources explicitly
granted and uploaded by users can appear. During the current rollout this gate
is false, so production link, sync, detail, history, statistics, and data
webhook payloads are unavailable.

After approval, the owner sees a prominent **Activate for production** action in
the dashboard and integration detail. That action changes the reviewed profile
from `approved` to `active` and makes it eligible for the mobile catalogue. It
does not enable the independent BOT production-data gate. BOT can suspend an
integration immediately, and administrators can activate or reactivate it for
operational recovery.

The separate data gate is enabled only after the mobile consent/upload release,
legal and operational checks, production credential tests, rollback tests, and
an explicit BOT go-live decision. Neither approval nor owner activation can
bypass it. These states also do not grant commercial-use rights; the separate
written agreement remains mandatory whenever the integration or surrounding
service is monetized.

## 9. Prepare production separately

After approval, request a new **production** certificate. Never reuse the
sandbox key/certificate, resource IDs, cursor, ETag, cached data, or webhook
endpoint state in production. Confirm through `GET /capabilities` that the
certificate is bound to the expected environment, scopes, quotas, status, and
production-data gate before reading anything else.

Pull is the canonical and recoverable transport. Start without a cursor, commit
the whole returned page locally, then store `next_cursor`. Poll every 15 minutes
unless the capability response documents an approved override.

Each upsert is a complete permitted projection, not a patch. Apply by monotonic
revision and resource ID. History is independently paginated. Treat
`history_complete=false` as a hard incomplete marker.

## 10. Optional webhooks

An integration can register up to three public HTTPS endpoints in each permitted
environment. Choose explicit events and either `notification` or
`full_payload`. BOT verifies endpoint ownership before activation. Redirects,
private/reserved addresses, unsafe DNS changes, and oversized responses are
refused.

Verify `Content-Digest` and the Ed25519 HTTP Message Signature before parsing the
body, enforce timestamp freshness, and deduplicate the event ID. Return a 2xx
only after durable acceptance. Pull sync remains the recovery source after
downtime, reordering, or a missed event.

During the current public sandbox rollout, scheduled webhook delivery is not a
production-available channel. Do not make webhooks a go-live dependency until
the portal and BOT explicitly show that the delivery worker is enabled for your
environment.

See [webhook operations](webhooks.md) and the downloadable AsyncAPI contract.

## 11. Handle consent and revocation correctly

Users grant exact orders and, optionally, exact stock vehicles for one
integration. Statistics participation is a separate opt-in for that exact
integration. The API response is always the intersection of the global
allowlist, reviewed scopes, current grant, and environment/client scopes.

Revocation blocks access immediately. A tombstone appears in the next sync and
may also produce a minimal webhook. Delete all individual copies and derived
individual data, then call `POST /revocations/{id}/ack` within seven days. Use a
stable `Idempotency-Key`; acknowledgements are retry-safe. An outstanding
deletion is warned at day five and can suspend production at day seven.

Never infer that a resource remains authorized merely because it still exists
in a local cache. The current sync/tombstone state is authoritative.

## 12. Operate within quotas and failure contracts

Baseline limits are 600 pull/detail requests per integration/hour, burst 20 per
minute, four concurrent requests, 500 records, and 8 MiB per response.
Statistics use 120 requests/hour and 2 MiB. Link-code redemption is additionally
limited to 60 attempts per integration/hour and 10 per source IP/15 minutes.
Approved overrides appear in `GET /capabilities`.

Inspect the rate-limit headers on every response. On `429`, honor
`Retry-After`. API errors use RFC 9457 `application/problem+json` with a stable
`code` and diagnostic `request_id`. Treat `401`/`403` as a certificate, status,
environment, or scope problem; treat `410` as a deletion requirement; retry
`503` only with bounded exponential backoff.

Monitor certificate expiry, quota usage, last successful sync, cursor commit,
history completeness, outstanding deletion acknowledgements, webhook dead
letters, and the current BOT webhook JWKS key IDs. The portal shows usage and
the deletion queue.

## 13. Data boundary

The API never returns a VIN value, real order number, BOT account/contact/address
or financing/payment data, Tesla IDs/tokens/credentials, raw provider payloads,
GPS/calendar/vehicle name, or door/window/lock and other presence-sensitive live
state. Individual data never enters the statistics endpoint.

Do not ask BOT to widen the contract through internal app interfaces or scrape
another BOT surface. A new field, scope, or purpose requires a reviewed profile
version and, where applicable, renewed user consent.

See the [field catalogue](field-catalog.md),
[consent and revocation](consent-and-revocation.md), and
[versioning policy](versioning.md) for the normative boundaries.

## 14. Keep licence, attribution, and terms acceptance current

Continue to comply with the
[BOT Integration Terms](https://integration.boringordertracker.com/terms) for
the entire integration lifecycle. When BOT publishes a new version, the
operator must review and explicitly accept that version; an old acceptance does
not cover the new text.

The default licence remains non-commercial. Do not add ads or AdSense, paid
features, subscriptions, sponsorships, affiliate monetization, resale, or any
other direct or indirect monetization without an individual written agreement
with BOT first. Notify BOT before the business model changes so the integration
can be reviewed against the correct agreement.

Keep the BOT credit and app link conspicuous, readable, and close to the data it
describes. Mixed-source output must continue to identify each BOT-derived
record, entry, series, or result at the point of display, including in exports
and other user-visible output. Review these requirements whenever the layout,
data source, presentation, or monetization model changes.
