Skip to main content

Migrating from the legacy API to v3

What changed​

  • Single versioned prefix. Everything now lives under /api/v3/*. The split between /api/1 and /api/2 is gone.
  • JSON-everywhere. All endpoints accept and return JSON, except POST /api/v3/builds/upload, which uses multipart/form-data. The legacy form-encoded body conventions (with webhook-name, webhook-url aliases, comma-separated actions, etc.) are dropped.
  • Stricter validation. Webhook URLs are SSRF-checked.
  • Sites became Teams. The Sites collection in v1 is the Teams collection in v3.

Endpoint map​

Builds​

Legacyv3Notes
GET /api/1/projects/{pid}/buildsGET /api/v3/projects/{pid}/builds—
GET /api/1/projects/{pid}/builds/{bid}GET /api/v3/builds/{id}Path flattened
PATCH /api/1/projects/{pid}/builds/{bid}PUT /api/v3/builds/{id}Method PATCH → PUT
DELETE /api/1/projects/{pid}/builds/{bid}DELETE /api/v3/builds/{id}—
POST /api/1/projects/{pid}/builds/{bid}/copyPOST /api/v3/builds/{id}/copyJSON body, no folder_name required
GET /api/1/projects/{pid}/builds/{bid}/downloadGET /api/v3/builds/{id}/downloadReturns a JSON url instead of a redirect
POST /api/1/projects/{pid}/builds/{bid}/invitesPOST /api/v3/builds/{id}/notify-testersRenamed to reflect what it actually does
POST /api/uploadPOST /api/v3/builds/uploadteam_id required unless project_id is given; folder_name → folder; app_version → version; release_notes only (no changelog/comment aliases); groups only (no app_permission_groups); returns 201 with the v3 build object
GET /api/1/projects/{pid}/builds/{bid}/symbols/downloadGET /api/v3/builds/{id}/symbols/downloadPath flattened

Tags change from a comma-separated string (v1) to a JSON array (v3).

Projects​

Legacyv3Notes
GET /api/1/projectsGET /api/v3/projectsv3 shape; v1's stripped-down shape is gone
GET /api/2/projectsGET /api/v3/projects—
GET /api/2/projects/{pid}GET /api/v3/projects/{id}—
GET /api/2/projects/{pid}/buildsGET /api/v3/projects/{id}/builds—
GET /api/2/projects/{pid}/testersGET /api/v3/projects/{id}/testersDirect project_tester rows + group members, deduped

Testers​

Legacyv3Notes
GET /api/1/testersGET /api/v3/testers—
POST /api/1/testersPOST /api/v3/testersJSON body
GET /api/1/testers/{id}GET /api/v3/testers/{id}—
DELETE /api/1/testers/{id}DELETE /api/v3/testers/{id}—
POST /api/1/testers/{id}/blockPOST /api/v3/testers/{id}/block—
DELETE /api/1/testers/{id}/blockDELETE /api/v3/testers/{id}/block—
caution

v3 tester IDs are membership IDs, not user IDs. Reusing a v1 tester ID hits the wrong tester or returns 404. Look testers up with GET /api/v3/testers?search=<email> and use the returned id; user_id is also returned.

Groups​

Legacyv3Notes
GET /api/1/groupsGET /api/v3/groups—
GET /api/1/groups/{gid}GET /api/v3/groups/{id}—
GET /api/1/groups/{gid}/testersGET /api/v3/groups/{id}/testers—
GET /api/1/groups/{gid}/projectsGET /api/v3/groups/{id}/projects—
GET /api/1/testers/groupsGET /api/v3/groupsMoved out of the testers namespace
POST /api/1/testers/groupsPOST /api/v3/groupsRequires team_id
POST /api/1/testers/groups/{gid}POST /api/v3/groups/{id}/testersJSON body with email
DELETE /api/1/testers/groups/{gid}DELETE /api/v3/groups/{id}/testers/{userId}The user ID (user_id from /api/v3/testers) is now in the path

Webhooks​

Legacyv3Notes
GET /api/1/webhooksGET /api/v3/webhooks—
POST /api/1/webhooksPOST /api/v3/webhooksJSON body; actions validated against upload, download, new-udid
GET /api/1/webhooks/{id}GET /api/v3/webhooks/{id}—
POST /api/1/webhooks/{id}PUT /api/v3/webhooks/{id}Method POST → PUT; status must be active or suspended
DELETE /api/1/webhooks/{id}DELETE /api/v3/webhooks/{id}—

Sites → Teams​

Legacyv3Notes
GET /api/1/sitesGET /api/v3/teamsResponse shape changes: {site:{accounts,managers}} becomes {teams, pagination}
GET /api/1/sites/{id}GET /api/v3/teams/{id}—
POST /api/1/sitesPOST /api/v3/teams—
DELETE /api/1/sites/{id}DELETE /api/v3/teams/{id}—

Audit​

Legacyv3Notes
GET /api/1/auditsGET /api/v3/audits—
GET /api/2/auditsGET /api/v3/audits—
GET /api/2/audits/admin-trailGET /api/v3/audits?action=<action_type>No role-based split — filter by the specific action_type string. Use GET /api/v3/audits/actions to enumerate valid values.
GET /api/2/audits/tester-trailGET /api/v3/audits?action=<action_type>Same as above — pick an action_type from GET /api/v3/audits/actions.

v3 action filters take one value, so make one call per action type to rebuild a trail.

Removed without replacement​

  • GET /api/1/cpanel/permissions — listed org admins under a permission shape that v3 doesn't track. Use GET /api/v3/testers instead.

Watching usage​

Every hit to a legacy route is logged at INFO with event legacy_api_hit, including the route name, HTTP method, user ID, an 8-char hash of the API key, and a duration in milliseconds. If you administer an org and want to know which of your integrations are still on the deprecated surface, search logs for legacy_api_hit filtered by api_key_hash.