Appearance
Assign a Cloud Credential to a Project
A Credential Assignment binds a Cloud Credential to a Project. The binding is request-and-approve: the request opens in the requested state, and the binding only becomes live once a second operator moves it to approved. There is no plexctl credential-assignment subcommand yet, so the /v1/.../credential-assignments surface is driven with curl.
Prerequisites
- An authenticated session — see Log in with plexctl.
${PLEXSPHERE_URL}and a bearer${TOKEN}.- A Project UUID. Opening a request needs the
adminormaintainerrelation on that Project — the Project is the residency pivot the request gate authorises against. - A Cloud Credential UUID in an assignable lifecycle state. A credential that is not assignable is rejected with
422 credential_not_assignable. - A second principal to approve the request. The approver must hold the
assignrelation on the Cloud Credential and may not be the principal that opened the request — a self-approval is rejected with403 self_approval_denied.
Steps
Request a Credential Assignment
shell
curl --silent --show-error --fail-with-body -X POST \
-H "Authorization: Bearer ${TOKEN}" -H 'content-type: application/json' \
"${PLEXSPHERE_URL}/v1/projects/<project-uuid>/credential-assignments" -d '{
"cloud_credential_id": "0190a8b8-a0c0-7a0a-8a0a-a0a0a0a0a0d1"
}'A 201 carries the new assignment in the requested state, and the Location header holds its canonical URL (/v1/credential-assignments/<assignment-uuid>). The binding is not yet materialised. A second open request for the same (Project, Cloud Credential) pair while an earlier one is still live is rejected with 409 duplicate_live_assignment.
List a Project's Credential Assignments
shell
curl -s -H "Authorization: Bearer ${TOKEN}" \
"${PLEXSPHERE_URL}/v1/projects/<project-uuid>/credential-assignments"The page returns creation-ordered lifecycle metadata for every assignment the caller may observe. Page with ?limit= (clamped to [1, 200]) and follow next_cursor with ?cursor=. A cursor minted by one principal cannot be replayed by another — the cross-caller replay surfaces as 403 cursor_binding_mismatch.
Approve a Credential Assignment
shell
curl -s -X POST -H "Authorization: Bearer ${TOKEN}" \
"${PLEXSPHERE_URL}/v1/credential-assignments/<assignment-uuid>/approve"Approval moves the assignment to approved and materialises the binding. It is only legal from the requested state — any other source state returns 409 illegal_transition. The requester may not approve their own request; that self-approval is rejected with 403 self_approval_denied, so a second operator must approve.
Reject a Credential Assignment
shell
curl -s -X POST -H "Authorization: Bearer ${TOKEN}" -H 'content-type: application/json' \
"${PLEXSPHERE_URL}/v1/credential-assignments/<assignment-uuid>/reject" -d '{
"reason": "Cloud Credential is scheduled for rotation this week"
}'Rejection closes an unapproved request — it is only legal from the requested state. The reason is recorded on the lifecycle event as an approver-supplied audit string; a whitespace-only value is rejected with 400 invalid_decision_reason.
Revoke a Credential Assignment
shell
curl -s -X POST -H "Authorization: Bearer ${TOKEN}" -H 'content-type: application/json' \
"${PLEXSPHERE_URL}/v1/credential-assignments/<assignment-uuid>/revoke" -d '{
"reason": "Project decommissioned by the platform on-call"
}'Revocation tears down a previously materialised binding — it is only legal from the approved state. As with reject, the reason is recorded on the lifecycle event and must be non-empty.
Verification
shell
curl -s -H "Authorization: Bearer ${TOKEN}" \
"${PLEXSPHERE_URL}/v1/projects/<project-uuid>/credential-assignments" \
| jq '.items[] | {id, cloud_credential_id, state, materialised}'After approval the row shows state: "approved" and materialised: true; after revocation it shows state: "revoked" and materialised: false.
See also
../../reference/provisioning/cloud-credential-assignments.md— the HTTP contract.../../contexts/provisioning/rebac.md— how the ReBACmanage/observechecks gate the lifecycle.