Appearance
Testers and groups
Testers use email links to reach builds shared with their groups. They do not need GitHub accounts or organization membership. The management routes here require a GitHub dashboard session; ReleaseDesk API tokens do not authenticate them. Use the organization's ID in every path.
Tester endpoints
All tester paths begin with /api/v1/orgs/:orgId/testers.
| Method and suffix | Minimum role | Success | Result |
|---|---|---|---|
GET / | Viewer | 200 | Paged tester list |
POST / | Developer | 201 | Created tester |
POST /import | Developer | 200 | Import counts |
PATCH /:testerId | Developer | 200 | Updated tester |
POST /:testerId/invite | Developer | 204 | Email sign-in invitation queued |
DELETE /:testerId | Developer | 204 | Empty body |
GET / accepts q, groupId, limit, and cursor query parameters. q searches for a case-insensitive substring of the email or name, up to 100 characters. groupId limits results to members of one group. limit defaults to 50 and may be 1–100. The response has items and nextCursor, which is null on the last page. Results are ordered by email; pass the returned cursor unchanged on the next request. It is an email value, not a tester ID or page number.
http
GET /api/v1/orgs/org_123/testers?groupId=tgr_456&limit=50Each tester contains id, email, name, status, groups, and createdAt. Group entries contain id, name, and color. status is none if no login link has been emailed, pending for an unused unexpired link, or active once a tester has signed in. Status reflects login-token history; it is not an approval or build-installation state.
Create, edit, and import
http
POST /api/v1/orgs/org_123/testers
Content-Type: application/json
{"email":"alex@example.com","name":"Alex Chen","groupIds":["tgr_456"]}The email is trimmed and converted to lowercase. name is optional and defaults to an empty string; it can be at most 120 characters. groupIds defaults to an empty array and accepts up to 100 IDs, all belonging to this organization. Creating the same email twice returns 409 tester_conflict. An unknown group returns 404 tester_group_not_found; exceeding a group's member limit returns 409 tester_group_full. The server also enforces an instance-configured maximum number of testers per organization, returning 422 validation_failed if exceeded.
PATCH /:testerId accepts name, groupIds, or both. When supplied, groupIds replaces the tester's complete group membership; use an empty array to remove them from every group. Omitting groupIds leaves memberships alone. The response is the updated tester. An unknown tester returns 404 tester_not_found.
Bulk import is a JSON operation, not a CSV upload:
http
POST /api/v1/orgs/org_123/testers/import
Content-Type: application/json
{"emails":["alex@example.com","sam@example.com"],"groupIds":["tgr_456"]}Send 1–500 email addresses. Duplicate addresses in the request count once after normalization. Existing testers are kept and added to the requested groups; the import does not overwrite their names or remove their existing group memberships. The response is { "created": 1, "existing": 1 } for the example if Alex already existed and Sam was new. The organization and group capacity checks still apply.
Invite or remove a tester
POST /:testerId/invite queues an email sign-in link for an existing tester and returns 204 No Content. The tester follows the link to the separate /t portal. Creating a tester or adding one to a group does not itself send this invitation. A build distribution can also email a build notification to members of the chosen groups.
DELETE /:testerId removes the tester record and returns 204. Removing a tester also removes their group membership through the database relationships. A missing ID returns 404 tester_not_found.
Group endpoints
All group paths begin with /api/v1/orgs/:orgId/tester-groups.
| Method and suffix | Minimum role | Success | Result |
|---|---|---|---|
GET / | Viewer | 200 | { "items": [group, ...] } |
POST / | Developer | 201 | Created group |
GET /:groupId | Viewer | 200 | One group |
PATCH /:groupId | Developer | 200 | Updated group |
DELETE /:groupId | Developer | 204 | Empty body |
GET /:groupId/members | Viewer | 200 | { "items": [tester, ...] } |
POST /:groupId/members | Developer | 204 | Members added |
DELETE /:groupId/members/:testerId | Developer | 204 | Member removed |
Create a group with a name and optional description, member cap, and color:
http
POST /api/v1/orgs/org_123/tester-groups
Content-Type: application/json
{"name":"Android beta","description":"Early Android builds","maxMembers":200,"color":"blue"}Names are trimmed, must be 1–80 characters, and are unique within the organization. Descriptions are at most 500 characters. maxMembers can be null for no cap or an integer from 1 to 10,000. Colors are gray, blue, green, amber, or red. Omitted values default to an empty description, no cap, and gray. The group response includes id, name, description, maxMembers, color, memberCount, and createdAt.
PATCH /:groupId changes only fields present in the body. The server rejects a new maxMembers smaller than the current memberCount with 409 tester_group_full. A duplicate name returns 409 tester_conflict. GET /:groupId/members returns every member as a tester object, sorted by email; this route is not paginated.
Add several existing testers in one call:
http
POST /api/v1/orgs/org_123/tester-groups/tgr_456/members
Content-Type: application/json
{"testerIds":["tst_111","tst_222"]}Send 1–500 IDs. All must belong to the organization. Adding someone already in the group has no additional effect. DELETE /:groupId/members/:testerId removes one membership; it does not delete the tester record. The delete returns 404 tester_not_found when that membership is absent. A missing group returns 404 tester_group_not_found on group-scoped operations.
After your testers and groups are ready, use Build distribution to share a release. For the recipient experience, see the tester portal.
Tester feedback
Testers submit feedback from the tester portal. Managers read it through the dashboard's Feedback tab or the API below. All paths begin with /api/v1/orgs/:orgId/apps/:appId/feedback and require a dashboard session.
| Method and suffix | Minimum role | Success | Result |
|---|---|---|---|
GET / | Viewer | 200 | { "items": [...], "nextCursor": string | null } |
GET /:id | Viewer | 200 | Feedback with attachment ids and URLs |
GET /attachments/:attachmentId | Viewer | 200 | Screenshot bytes |
PATCH /:id | Developer | 200 | Updated feedback; body { "status": "new" | "triaged" | "resolved" } |
DELETE /:id | Developer | 204 | Deletes the row and its stored screenshots |
GET / accepts releaseId, status, limit (1–100, default 50) and cursor. Attachments are served with their stored image type, X-Content-Type-Options: nosniff and a restrictive Content-Security-Policy. Status changes and deletions are written to the audit log.