Skip to main content

API Reference (v3)

Use the REST API to integrate app distribution into your CI/CD pipeline.

Migrating from TestFairy? See Legacy API (v1) and the API Migration Guide.

Authentication​

All API requests require authentication via one of the following methods:

MethodExample
X-API-Key headercurl -H "X-API-Key: YOUR_KEY" ...
Bearer tokencurl -H "Authorization: Bearer TOKEN" ...
api_key form parametercurl -F api_key=YOUR_KEY ...
HTTP Basic (email:apiKey)curl -u you@example.com:YOUR_KEY ...
OIDCcurl -H "Authorization: Bearer JWT" -H "X-OIDC-Config-Key: CONFIG_KEY" ...

Find your API key by clicking the key icon in the top navigation bar. You can exchange it for a short-lived Bearer token via POST /api/v3/auth/token.

Pagination​

All list endpoints support pagination via query parameters:

ParameterDefaultDescription
page1Page number
per_page25Results per page (max: 100)

Larger per_page values are capped at 100, except on /api/v3/audits, which returns a validation error instead.

Paginated responses include a pagination object. The list key matches the resource - projects, builds, teams, testers, groups, webhooks, or audits:

{
"projects": [...],
"pagination": {
"page": 1,
"per_page": 25,
"total": 142,
"total_pages": 6
}
}

Endpoints​

Authentication​

MethodEndpointDescription
POST/api/v3/auth/tokenExchange API key for a 1-hour Bearer token

Apps​

MethodEndpointDescription
GET/api/v3/projectsList all apps (paginated)
GET/api/v3/projects/{id}Get an app
POST/api/v3/projectsCreate an app
PUT/api/v3/projects/{id}Update an app
DELETE/api/v3/projects/{id}Delete an app (admin)
GET/api/v3/projects/{id}/testersList testers assigned to an app (direct + via groups, deduped)
POST/api/v3/projects/{id}/copyCopy an app

Builds​

MethodEndpointDescription
GET/api/v3/projects/{projectId}/buildsList builds for an app (paginated)
GET/api/v3/builds/{id}Get a build
POST/api/v3/builds/uploadUpload a new build (multipart/form-data)
PUT/api/v3/builds/{id}Update release notes, tags, and the app's landing page visibility and URL alias
GET/api/v3/builds/{id}/downloadGet pre-signed download URL. If storage isn't configured, returns the install-page URL instead.
DELETE/api/v3/builds/{id}Delete a build (admin)
POST/api/v3/builds/{id}/copyDuplicate a build within the same app (references the same file)
POST/api/v3/builds/{id}/notify-testersQueue the new-build email to testers. Returns 202 {"status":"queued"}, or 409 if the build isn't distributable. Requires admin rights on the app.
GET/api/v3/builds/{id}/symbols/downloadGet a download URL for the build's symbols file

Upload Parameters​

POST /api/v3/builds/upload takes multipart/form-data:

ParameterRequiredDefaultDescription
fileYes—The build file itself (.apk, .aab, .ipa, or .zip), sent as a multipart file part, not a text field. A URL or path in a text field is rejected with 400 {"error": "file is required."}
project_idSee note—ID of the app to upload to. When set, team_id is ignored
team_idSee note—ID of the team to upload to. Required unless project_id is given. The build goes to that team's existing app for the package name, or a new app is created there
folderNoNoneFolder name. Scopes the package-name lookup and is applied to an auto-created app
groupsNoUnchangedComma-separated tester group names or IDs to grant every build of the app to. Replaces existing app-level grants; omit to leave grants unchanged, or send none to remove all app-level grants. Build-level grants from build_groups are not affected. Names that don't resolve are returned in invalid_groups. If none resolve, grants are left unchanged; if some do, only those are applied, so a misspelled group loses its access. Does not send email on its own (see notify).
build_groupsNoUnchangedComma-separated tester group names or IDs to grant access to this build only. Adds to existing grants; a group not listed keeps its access, and omitting the parameter changes nothing. A group that already has app-level access is still granted this build, so it keeps the build if that app-level grant is later removed. Send none (case-insensitive, as the only value) to remove this build's own grants; app-level grants from groups stay. Names that don't resolve are returned in invalid_build_groups. Independent of groups and notify. Ignored for multi-app zip uploads. Replaces legacy testers_groups, testers-groups and invitation_groups
landing_page_modeNoUnchangedLanding page visibility: open or closed
landing_page_slugNoUnchangedURL alias for the app's landing page. 6-63 characters: letters, digits, dot, hyphen, or underscore, not starting or ending with a separator. Returns 409 if the alias is already used by another app
notifyNooffSet to on (or 1) to email the app's tester groups about the new build. Granting groups does not notify them on its own. Ignored for builds that can't be installed (for example, generic files)
release_notesNoNoneRelease notes for the build
symbols_fileNoNoneDebug symbols to attach (iOS dSYM or Android mapping file). Best-effort: an invalid symbols file never fails the upload.
sync_to_saucelabsNooffSet to on to also copy the build to Sauce Labs App Storage. Requires a Sauce Labs connection on the organization
versionNoDetected from the fileOverride the version string
version_codeNoDetected from the fileOverride the version code

Provide either project_id or team_id.

note

iOS apps signed with an ad-hoc or development certificate are closed beta unless your organization turns on Allow ad-hoc apps to be open beta in Organization Settings. Without that setting, landing_page_mode=open is ignored for these apps, and the upload still succeeds. A landing page that an upload creates for such an app starts closed; send landing_page_mode=open to open it after the setting is on. If Require login before download is on, every app is closed beta, whatever landing_page_mode says.

curl -X POST https://your-org.testfairy.com/api/v3/builds/upload \
-H "X-API-Key: $API_KEY" \
-F project_id=$PROJECT_ID \
-F file=@app-release.apk \
-F notify=on
note

The API does not fetch builds from a URL. Download the file first (for example from your own S3 bucket), then send its contents as the file part.

Update Parameters​

PUT /api/v3/builds/{id} takes a JSON body. Omitted fields are left unchanged. Testers can't update builds (403).

ParameterTypeDescription
release_notesstringRelease notes for the build
tagsarray of stringsReplaces the build's tags
landing_page_modestringLanding page visibility for the build's app: open or closed. Any other value returns 400
landing_page_slugstringURL alias for the app's landing page, same rules as on upload. Also accepted as community_token. Returns 400 if invalid, 409 if another app already uses it
curl -X PUT https://your-org.testfairy.com/api/v3/builds/$BUILD_ID \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"release_notes": "Bug fixes", "landing_page_mode": "closed"}'

landing_page_mode and landing_page_slug change the whole app, not just this build. The ad-hoc rules in the upload note above apply to this endpoint too: without Allow ad-hoc apps to be open beta, open is ignored for ad-hoc apps and the request still succeeds.

Teams​

MethodEndpointDescription
GET/api/v3/teamsList all teams (paginated)
GET/api/v3/teams/{id}Get a team
POST/api/v3/teamsCreate a team (admin)
PUT/api/v3/teams/{id}Update a team (admin)
DELETE/api/v3/teams/{id}Delete a team (admin)

Testers​

MethodEndpointDescription
GET/api/v3/testersList testers (paginated, searchable)
GET/api/v3/testers/{id}Get a tester
POST/api/v3/testersInvite a tester by email (admin). Only the Tester role can be created via the API.
DELETE/api/v3/testers/{id}Remove a tester (admin)
POST/api/v3/testers/{id}/blockBlock a tester (admin)
DELETE/api/v3/testers/{id}/blockUnblock a tester (admin)

Groups​

MethodEndpointDescription
GET/api/v3/groupsList all groups (paginated)
GET/api/v3/groups/{id}Get a group with testers and apps
POST/api/v3/groupsCreate a group (admin)
PUT/api/v3/groups/{id}Update a group (admin)
DELETE/api/v3/groups/{id}Delete a group (admin)
POST/api/v3/groups/{id}/testersAdd tester to group (admin)
DELETE/api/v3/groups/{id}/testers/{userId}Remove tester from group (admin)
GET/api/v3/groups/{id}/testersList testers in a group (paginated)
GET/api/v3/groups/{id}/projectsList apps the group has access to (paginated)

Webhooks​

MethodEndpointDescription
GET/api/v3/webhooksList all webhooks (paginated)
GET/api/v3/webhooks/{id}Get a webhook
POST/api/v3/webhooksCreate a webhook (admin)
PUT/api/v3/webhooks/{id}Update a webhook (admin)
DELETE/api/v3/webhooks/{id}Delete a webhook (admin)

Settings​

MethodEndpointDescription
GET/api/v3/settings/oidcGet the organization's OIDC configuration (admin)
POST/api/v3/settings/oidcCreate or update the OIDC configuration (admin)
DELETE/api/v3/settings/oidcDelete the OIDC configuration (admin)
POST/api/v3/settings/oidc/testTest OIDC discovery against the issuer (admin)

Audit Logs​

MethodEndpointDescription
GET/api/v3/auditsList audit logs (paginated, filterable by action/search/date, admin)
GET/api/v3/audits/actionsList distinct audit action types (admin)