Register a site
const url = 'https://example.com/v1/projects/example/sites';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"displayName":"example","origin":"example","approvalsUrl":"example","allowedRoles":["example"],"roleMap":{"additionalProperty":{"ask":true,"reviewer":"standard"}},"reviewerCeiling":"standard","vouching":true,"peopleCap":1,"newPeoplePerDay":1}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/projects/example/sites \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "displayName": "example", "origin": "example", "approvalsUrl": "example", "allowedRoles": [ "example" ], "roleMap": { "additionalProperty": { "ask": true, "reviewer": "standard" } }, "reviewerCeiling": "standard", "vouching": true, "peopleCap": 1, "newPeoplePerDay": 1 }'A site that will sign its own people in to this project, and a one-time enrollment code for it, shown once: the site posts it with its public key (POST /v1/auth/sites/enroll). Until then it has no key and signs nobody in. A role map gives no reviewer role above reviewerCeiling, and names only allowedRoles; nothing it gives reaches above the project. Tenant admins only.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”ProjectId — opaque branded string.
Header Parameters
Section titled “Header Parameters”Caller-supplied idempotency key, scoped to the caller: the same key from someone else in the tenant is their own request (from 0.1.5). Retries with the same key return the original response byte-identical (per docs/API-ROUTE-CONVENTIONS.md §3.1). A retry sent while the first request still runs is answered 409 idempotency-key-in-flight with Retry-After, on a runtime whose store holds keys (from 0.1.5); retry after it, and you get the first request’s answer. An answer that carries a secret (a new API key or public run token, a session token, a generated signing secret) isn’t kept: a retry gets 409 idempotency-key-replay-withheld, with the first request’s status and when it succeeded (from 0.1.5).
Request Bodyrequired
Section titled “Request Bodyrequired”object
An https:// origin with no path; http:// only for localhost, 127.0.0.1 or [::1].
A URL on the site’s own origin with {approvalId} in it.
The roles that may sign in at all. Default ["administrator", "shop_manager"].
Default: both default roles may ask, and decide as standard reviewers. Without a roleMap, the map keeps only the entries of the roles in allowedRoles: the default map’s for a new site, the site’s own for a change. An allowed role with no entry signs in to read the project only.
object
What a person with one of the site’s roles gets in the site’s project. Reading the project comes with every role; nothing above it ever does.
object
Whether they may start turns with the project’s agents.
The reviewer role they decide approvals with, at most the site’s reviewerCeiling; absent: they decide none.
Default standard.
Default true.
Default 50.
Default 20.
Responses
Section titled “Responses”The site, and its enrollment code.
object
A site that signs its own people in to one project: it vouches for whoever is signed in there, so they ask the project’s agents and decide its approvals as themselves. Each person it signs in is a person of the tenant, shown as “
object
Set by a tenant admin, never by the site: shown after every name the site signs in.
The site’s origin (https://acme-store.example).
Where an approval opens on the site; {approvalId} is replaced. A site’s people get it in their reviewer email.
Role → grant. A person with several allowed roles gets the most any of them gives.
object
What a person with one of the site’s roles gets in the site’s project. Reading the project comes with every role; nothing above it ever does.
object
Whether they may start turns with the project’s agents.
The reviewer role they decide approvals with, at most the site’s reviewerCeiling; absent: they decide none.
Whether the site’s statements are accepted.
At most this many live people from the site.
At most this many new people from the site per day.
One of the site’s live public keys. Kindgi never has a private half.
object
The key’s RFC 7638 thumbprint, to compare with the one the site shows.
What the site’s statements carry as aud: this deployment’s public URL, given at enrollment.
A one-time code the site posts with its public key (POST /v1/auth/sites/enroll). Shown once. Using it replaces the site’s keys and ends its live sessions.
object
Example
{ "site": { "roleMap": { "additionalProperty": { "reviewer": "standard" } }, "reviewerCeiling": "standard" }}A setting the site can’t have (site-invalid, details.field): an origin that isn’t https, an approvals URL on another origin, a role map above the ceiling or over a role that isn’t allowed.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Missing / malformed / expired / revoked bearer token.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Not a tenant admin.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}No project with that id (project-not-found).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Idempotency-Key reused with a different body (idempotency-key-body-mismatch), or a retry of a request that succeeded: its answer carried a secret, which isn’t kept (idempotency-key-replay-withheld).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The request body is over the API’s limit (request-too-large; details.maxBytes says how much).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Server error (unmapped domain code or framework crash).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}See also
GET /v1/projects/{projectId}/sites: List the project's sitesGET /v1/projects/{projectId}: Fetch a project by idPATCH /v1/projects/{projectId}: Partially update a projectDELETE /v1/projects/{projectId}: Delete a project (idempotent)- Error code
idempotency-key-body-mismatch - Error code
idempotency-key-replay-withheld - Error code
project-not-found - Error code
request-too-large - Error code
site-invalid - Every error code