인증
Bearer 토큰으로 API 키를 전송하세요. 이 엔드포인트는 명세에 특정 권한을 명시하지 않으므로, 가정하기보다는 키에 필요한 최소한의 권한을 부여하고 응답을 확인하세요.
이 엔드포인트는 조직 ID를 받지 않습니다. 사용자의 키가 이미 속한 조직을 식별하며, 응답은 해당 조직으로 한정됩니다.
무료 체험하기
대괄호 안에 있는 모든 내용을 사용자 지정 값으로 바꾸고, 키 플레이스홀더는 대시보드의 키로 바꾸세요.
curl -X POST https://api.zinndigital.com/v1/sites/{siteId}/collaborators \
-H "Authorization: Bearer zdk_live_…" \
-H "Content-Type: application/json" \
-d '{ "email": <string>, "role": <SiteCollaboratorRole>, "reason": <string> }'로그인하셨나요? 대시보드의 API 콘솔이 실제 조직 ID와 본인의 키를 자동으로 채우고 라이브 API를 대상으로 요청을 실행하므로 실제 응답을 확인할 수 있습니다. API 콘솔에서 이 엔드포인트를 여세요
상세 정보
Grants one person access to this one site at one role, optionally until a date, and gives them the narrow `site_collaborator` organisation membership if they have none. The narrowing is enforced by Postgres row-level security on the site table itself, not by a filter in a query, so it applies to every endpoint on this API rather than to the ones somebody remembered. ⭐ **The person need not have an account yet.** An address nobody has signed in as is invited in the same call: an ordinary organisation invitation is sent at the `site_collaborator` role, and the grant is returned with `status: pending` and a null `user_id`. The moment they accept — which proves control of that inbox through the identity provider, so no id is ever guessed — the grant activates for that person. A pending grant narrows nobody: nothing is reachable until the accept. If the address already holds a pending invitation to the *whole* organisation the call is refused with `SITE_COLLABORATOR_ALREADY_INVITED` rather than quietly downgrading that invitation to one site. **What a collaborator may then do is the per-site role, on that site only.** A request whose path names a granted site is evaluated with the role's keys (`viewer` reads; `editor` adds `hosting.deploy.manage`, `sites.cache.purge`, `hosting.performance.manage`, `sites.wp_login`, `sites.panel_access`, `sites.restart`, `hosting.php.manage`; `manager` adds `hosting.backup.manage`, `hosting.ssl.manage`, `hosting.cdn.manage`, `hosting.applications.manage`, `hosting.import.manage`). A request naming any other site answers `404`, and a request naming no site — the collection endpoints and every organisation-level surface such as members, API keys and billing — carries only `sites.view`, so it is refused with `403`. Revoking a grant takes effect on the next request, whatever token the person is holding. Every refusal carries its OWN `error.code`, because the status says what happened and only the code says what to do about it. `409` is about the world: `SITE_COLLABORATOR_ALREADY_GRANTED` (this person already has a live or pending grant here), `SITE_COLLABORATOR_ALREADY_A_MEMBER` (they are already an unrestricted member, so a grant would imply a restriction we are not applying — a lie in an access table) and `SITE_COLLABORATOR_ALREADY_INVITED` (above). `422` is about the request: `SITE_COLLABORATOR_EXPIRY_IN_THE_PAST` and `SITE_COLLABORATOR_REASON_REQUIRED`. ⛔ Key on the code, never on `error.message`. The message is a sentence written for a person and it is translated; the code is the contract.
매개변수
| 이름 | 유형 | 필수 | 설명 |
|---|---|---|---|
siteId (path) | Uuid | 예 | Site ID (UUIDv7). |
요청 본문
| 이름 | 유형 | 필수 | 설명 |
|---|---|---|---|
email | string | 예 | The person to share with. An address with no account here is invited in the same call and the grant is returned `pending` — see the endpoint description. |
role | SiteCollaboratorRole | 예 | What a collaborator may do on the site they were granted. `viewer` reads it; `editor` adds the day-to-day work (deploys, cache, plugins, staging); `manager` adds the structural… |
reason | string | 예 | Why they are being given this site. Required rather than optional: an optional reason is one nobody fills in, and the audit value of the record collapses to *"somebody granted s… |
expires_at | object | 아니요 | Optional automatic end. Null means until revoked. A time in the past is refused with `expiry_in_the_past` rather than silently creating a dead grant. |
응답
| 이름 | 유형 | 필수 | 설명 |
|---|---|---|---|
id | string | 예 | — |
site_id | string | 예 | — |
user_id | object | 예 | The collaborator, once known. Null while the grant is `pending` — the address has been invited and nobody has signed in as it yet. |
email | string | 예 | — |
role | SiteCollaboratorRole | 예 | What a collaborator may do on the site they were granted. `viewer` reads it; `editor` adds the day-to-day work (deploys, cache, plugins, staging); `manager` adds the structural… |
reason | string | 예 | Why this person was given this site. Required on creation, and returned here because a year later it is the only thing that can answer *"why does this person have my site?"*. |
granted_by | string | 예 | The audit ref of whoever granted it, `user:<id>`. |
created_at | string | 예 | — |
expires_at | object | 예 | When the grant lapses on its own, or null for *until revoked*. Evaluated at read time, so it takes effect on the clock rather than when a sweep next runs. |
active | boolean | 예 | Neither revoked nor past its expiry, as of this response. |
status | string<pending, active, expired, revoked> | 예 | `pending` — invited, waiting for the person to sign in as that address; `active` — in force now; `expired` — lapsed on its own end date; `revoked` — ended by the owner, the row… |
이 엔드포인트가 반환할 수 있는 오류
401 · 403 · 404 · 409 · 422 · 429