Skip to main content
An install is one customer’s grant of access to your platform app: which scopes they approved, whether they consented to Work Email sharing, and the tokens minted under that grant. Every Platform API token (ot_ptk_…) belongs to exactly one install. Send your customer (an OpenTrain employer — specifically the organization owner) to:
https://app.opentrain.ai/integrations/{partnerSlug}/connect?external_project_id=<id>&redirect_uri=<url>&state=<opaque>
Query parameterRequiredPurpose
external_project_idNoYour project identifier. Echoed back on the return redirect so you know which of your projects initiated the connection.
redirect_uriNoWhere to send the customer after consent. Must be http(s) and match a redirect URI registered for your app: same origin, and its path must start with the registered base path (so dynamic sub-paths under a registered base work).
stateNoOpaque value echoed back unchanged on the return redirect — use it to correlate the flow on your side.
On the consent screen the customer sees your app’s name, the scopes you requested, and — if you requested participants:email — a separate, explicit PII consent checkbox for Work Email sharing. They can approve or decline.

The One-Time Token

When the customer approves, OpenTrain mints an install-scoped ot_ptk_… token and displays it once, on the consent screen only. The customer copies it into your platform’s integration settings.
The token never travels in the redirect back to your platform. The return redirect carries only opentrain_install_id, plus your echoed state and external_project_id — never credentials. If the token is lost, the customer must reconnect to mint a new one.

Scopes

Your app requests a subset of these seven scopes when you register it; the customer sees each one described on the consent screen:
ScopeGrants
project-links:readRead links between your projects and OpenTrain jobs
project-links:writeCreate and remove project links (implies project-links:read)
contracts:readRead contracts (status, dates, budget) on linked jobs
participants:readRead hired participants on linked contracts: OpenTrain user ID, display name, profile URL, country
participants:emailRead the participant’s @opentrain.work Work Email. Requires the explicit PII consent checkbox at install time; personal email addresses are never shared
usage:writeReport cumulative per-day work usage (time, tasks, labels) for contracts on linked jobs
webhooks:manageCreate, list, update, and delete webhook endpoints
participants:email is double-gated: the scope must be granted and the install must have piiConsent: true. If the customer granted the scope but left the consent box unchecked, requests that would return Work Email are refused with 403 at request time — and webhook payloads simply omit the workEmail field.

Inspecting Your Install

GET /installs/current returns the install your token belongs to — useful as a connectivity check and to confirm which scopes and consent you actually have:
curl -sS https://app.opentrain.ai/api/partner/v1/installs/current \
  -H "Authorization: Bearer $OT_PARTNER_TOKEN"
{
  "install": {
    "id": "<INSTALL_ID>",
    "status": "ACTIVE",
    "scopes": [
      "project-links:read",
      "project-links:write",
      "contracts:read",
      "participants:read",
      "participants:email",
      "webhooks:manage"
    ],
    "piiConsent": true,
    "organizationId": "<ORG_ID>",
    "createdAt": "2026-06-12T10:00:00.000Z",
    "partnerApp": { "id": "<APP_ID>", "slug": "your-app", "name": "Your App" },
    "token": { "id": "<TOKEN_ID>", "name": "Your App connection", "scopes": ["..."] }
  }
}

What Every Request Requires

Beyond the right scope, every Platform API request checks four things. Any of them failing yields 401 or 403:
  1. A live token — not revoked or expired
  2. An ACTIVE install
  3. An ACTIVE platform app
  4. The annotation-platform feature enabled for the granting employer’s account

Reconnecting

If a customer runs the consent flow again for an app they already installed, OpenTrain revokes all previous tokens for that install before minting the fresh one. A scope reduction on reconnect therefore cannot be bypassed by holding on to an older, broader token. Treat any 401 as a signal to ask the customer to reconnect.

Revocation

Customers can disconnect your app at any time from their OpenTrain integrations page. Disconnecting immediately:
  1. Flips the install to REVOKED
  2. Revokes every access token — your next request returns 401
  3. Emits an install.revoked event to webhook deliveries already queued
  4. Stops all new event fan-out for that install
Handle install.revoked by ceasing work for that customer and marking the connection as disconnected in your UI.