Architecture
The integration is a pure webhook consumer, platform-side only:- It talks to OpenTrain exclusively through the Platform API (
Authorization: Bearer ot_ptk_…) and signed webhooks. - Its only imports are
node:crypto(signature verification) andnode:http(the listener) — no SDKs, no frameworks. - It has three subcommands:
link,subscribe, andrun.
Prerequisites
- An annotation tool (or workspace) whose API you administer.
- A tool-side API token with administrator rights over organization membership. In many tools, a plain member’s token can read and create users but gets
403when removing memberships — offboarding silently breaks. Test the removal path with your token before going live. - An OpenTrain platform token with
project-links:write,contracts:read,participants:read,participants:email, andwebhooks:manage— and an install granted with PII consent, since provisioning keys off the AI trainer’s Work Email.
Configuration
Every setting is an environment variable with a matching CLI flag:| Env var | Flag | Used by | Value |
|---|---|---|---|
OPENTRAIN_BASE_URL | --opentrain-url | all | https://app.opentrain.ai |
OPENTRAIN_PARTNER_TOKEN | --partner-token | link, subscribe (optional for run) | ot_ptk_… from the consent screen |
OPENTRAIN_WEBHOOK_SECRET | --webhook-secret | run | whsec_… from endpoint creation |
TOOL_BASE_URL | --tool-url | link, run | your tool’s API origin, e.g. http://localhost:8080 |
TOOL_API_TOKEN | --tool-token | link, run | tool-side admin API token |
Step 1: Connect
The customer (an OpenTrain employer) opens your consent deep link, approves the scopes plus the PII consent checkbox, and pastes the one-timeot_ptk_… token into your configuration.
Step 2: Link a Project
link command reads the project from your tool’s API and registers it as an OpenTrain project link, so contract events for that job route to this integration:
Step 3: Subscribe
contract.started, contract.ended, project_link.removed, and install.revoked, then prints the one-time whsec_… secret:
Step 4: Run the Listener
Verify the Signature
Raw bytes, constant-time compare, 300-second drift window — rejected deliveries get401:
Dedupe and Acknowledge
Retries reuse the sameX-OpenTrain-Delivery ID, so the listener keeps a bounded set of processed IDs and answers 200 duplicate for repeats. Successful handling returns 200; a thrown error returns 500, which makes OpenTrain retry with backoff (1m, 5m, 30m, 120m).
Handle Contract Events
contract.started finds or creates the workspace user by Work Email; contract.ended removes their organization membership:
Fall Back to the Participants Endpoint
If the payload lacksworkEmail (or arrived before consent was sorted out), the handler pulls GET /contracts/{contractId}/participants — the same consent gates apply, but this makes the handler robust to redelivered or hand-replayed events:
Failure Handling
| Failure | What happens | What you do |
|---|---|---|
| Handler throws (e.g. your tool’s API briefly down) | Listener returns 500; OpenTrain retries up to 5 attempts | Usually nothing — a later retry succeeds |
| Listener down past the retry window | Deliveries marked FAILED; 10 consecutive failures auto-disable the endpoint | PATCH {"status": "ACTIVE"}, then redeliver all FAILED |
| Events from before the subscription existed | Never delivered — no backfill | Reconcile via GET /contracts?status=active |
Tool-Side Gotchas
These came up validating the consumer against a real self-hosted tool — check the equivalents in yours:- User-list response shapes vary across versions. Some builds return a plain array, others
{ "results": [...] }. Handle both when searching for a user by email. - “Cannot remove self” surfaces oddly. The tool refused to remove the token owner from their own organization with a
405rather than a clear error — treat unexpected statuses on the removal path as a distinct outcome, not a generic failure. - Use an organization admin’s token. Membership removal returned
403for plain members, which breaks offboarding while provisioning appears to work. - Check which auth scheme your token uses. Some tools ship multiple token systems (e.g. a legacy
Authorization: Token <key>header that must be explicitly enabled) — verify the scheme your integration uses is switched on.