Skip to content

POST /organization/token/{profile}

The POST /organization/token/{profile} endpoint returns GitHub installation tokens in JSON format, using token permissions granted by a specified organization profile.

This endpoint provides explicit control over which organization profile is used when vending GitHub tokens. Profiles allow configuring different sets of repositories and permissions for different use cases.

HeaderRequiredDescription
AuthorizationYesBearer token containing JWT
Content-TypeYesapplication/json

The {profile} path parameter specifies which organization profile to use. Profile names are used directly without prefixes.

Examples:

  • POST /organization/token/deploy
  • POST /organization/token/package-registry
  • POST /organization/token/buildkite-plugin

The API does not use prefixes. Prefixes like org: are part of the plugin interface only and are translated by the plugins to the appropriate API paths.

For profiles configured with repositories: ["{{caller-scoped-repository}}"] (see caller-scoped repositories), the target repository is supplied via the repository-scope query parameter:

POST /organization/token/agent-pr?repository-scope=widget

Supplying repository-scope for a profile that is not caller-scoped, or omitting it for one that is, returns 400 Bad Request.

The request body is expected to be empty.

{
"organizationSlug": "my-org",
"profile": "org:release-publisher",
"repositoryUrl": "",
"repositories": { "names": ["owner/release-tools", "owner/shared-infra"] },
"permissions": ["metadata:read", "contents:write", "packages:write"],
"app": "packages",
"token": "ghs_...",
"hashedToken": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"expiry": "2025-12-21T10:00:00Z"
}

For wildcard profiles (configured with repositories: ["*"]), the repositories field is {"wildcard": true} rather than a named list.

FieldTypeDescription
organizationSlugstringBuildkite organization from JWT claims
profilestringProfile identifier that was used
repositoryUrlstringAlways empty for organization profile requests
repositoriesobjectRepositories the token has access to. Either {"wildcard": true} (all repositories accessible to the GitHub App installation) or {"names": ["owner/repo", ...]} (specific named repositories).
permissionsarrayPermissions granted. Always includes metadata:read plus configured permissions.
appstringName of the GitHub App the token was created through. default when the default app is used. See using multiple GitHub Apps.
tokenstringGitHub installation token (format: ghs_...)
hashedTokenstringSHA-256 hash of the token, base64-encoded (base64(SHA-256(token))). Use to correlate with GitHub organisation audit log events for the same token.
expirystringISO 8601 timestamp when token expires

appId and installationId are included only when DEV_DISCLOSE_APP_IDENTIFIERS is set, a development-only setting.

When the requested repository is not in the profile’s repository list, the endpoint returns a successful empty response. This allows credential helpers to fall through to other authentication methods.

Status codeConditionResponse
400 Bad RequestInvalid profile format, or repository-scope missing/unexpected/malformedJSON error
401 UnauthorizedMissing or invalid JWTJSON error
403 ForbiddenInsufficient JWT claims, or GitHub rejected a caller-scoped repositoryJSON error
404 Not FoundProfile does not exist, or is unavailable because it failed validation (for example, it names a GitHub App that is not configured or is disabled)JSON error
500 Server ErrorToken vending error, GitHub error, or the profile names a GitHub App that could not be resolvedJSON error

A profile that failed validation returns {"error": "profile unavailable: validation failed"}. The cause is recorded only in the audit log.