Managing Service Accounts¶
Service Accounts are created and managed exclusively by users with the Super Admin role. Navigate to Admin → Service Accounts to access the management interface.
Creating a Service Account¶
Navigate to Admin → Service Accounts → Add to create a new account. You will be prompted to provide:
| Field | Required | Notes |
|---|---|---|
| Name | Yes | A human-readable label, e.g. CI/CD Pipeline — Prod |
| Description | No | Optional notes for operators |
| Status | Yes | Active or inactive; defaults to active |
| Scopes | Yes | One or more of read, write, admin |
| Allowed IP Addresses | No | One IP or CIDR range per line; leave empty to allow all IPs |
| Expiry Date | No | Must be a future date if set |
When the account is created, the client_id and client_secret are displayed once only in a dismissible confirmation dialog. Store both values securely — the secret cannot be retrieved after dismissal. If lost, you must regenerate a new secret from the account's management page.
Viewing and Editing an Account¶
Open an account from the Service Accounts list to view its details. All fields except client_id can be edited:
- Name and description can be updated at any time.
- Status can be toggled between active and inactive.
- Scopes can be modified to expand or restrict access.
- Allowed IP addresses can be updated to add or remove IPs and CIDR ranges.
- Expiry date can be set, changed, or cleared.
All changes are recorded in the audit log with the previous and updated values.
Regenerating a Secret¶
Open the account from the Service Accounts list and use the Regenerate Secret action. The new secret is displayed once in a confirmation dialog; the previous secret is immediately invalidated.
Any active sessions authenticated with the old secret continue to work until they expire. Sessions are not invalidated on secret regeneration — revoke them explicitly if immediate revocation is required.
Disabling an Account¶
Set the account status to inactive to suspend access without removing the account or its audit history. All subsequent authentication attempts will be rejected with SERVICE_ACCOUNT_INACTIVE.
Active sessions for a disabled account are immediately invalidated — the session validation middleware checks the account's current status on each request.
Deleting an Account¶
Use the Delete action on the account's management page to permanently remove the account. All active sessions for the account are immediately invalidated.
Deletion is permanent. If you want to preserve the audit history and the option to re-enable access later, disable the account instead.
Audit Log¶
All Service Account management operations are recorded in the platform audit log:
| Event | Trigger |
|---|---|
service_account.created |
Account created |
service_account.updated |
Account fields modified (includes old and new values) |
service_account.secret_regenerated |
Client secret regenerated |
service_account.deleted |
Account permanently deleted |
service_account.authenticated |
Successful authentication |
service_account.session_revoked |
Session explicitly revoked |
Managing Couriers via API¶
Service Accounts with write scope can create and manage Courier records programmatically. This is the primary integration point for Terraform providers and infrastructure-as-code pipelines that need to provision Couriers alongside the systems they serve.
All Courier management endpoints are prefixed /api/v1/service/couriers and require a valid session token.
Endpoints¶
| Method | Path | Required Scope | Description |
|---|---|---|---|
GET |
/api/v1/service/couriers |
read |
List all Couriers for the tenant |
GET |
/api/v1/service/couriers/{uuid} |
read |
Get a single Courier by UUID |
POST |
/api/v1/service/couriers |
write |
Create a Courier |
PUT |
/api/v1/service/couriers/{uuid} |
write |
Update a Courier |
DELETE |
/api/v1/service/couriers/{uuid} |
write |
Delete a Courier |
All resources are identified by UUID. Internal integer IDs are never exposed.
Important Behaviours¶
Authentication method is immutable. Once a Courier is created, its auth_method cannot be changed via the API or the portal. To change the authentication method, delete the Courier and recreate it. In Terraform, auth_method is a ForceNew attribute.
Client secret is returned once. For client_secret authentication, the client_id is auto-generated and included in all read responses. The client_secret is returned only in the POST creation response. Store it securely — it cannot be retrieved again. To rotate, use the portal's Regenerate Secret action.
PKI certificate not returned in reads. For pki_certificate authentication, the full PEM certificate is accepted on create and update but never returned in API responses. Only the certificate_fingerprint (SHA-256) is returned.
Domain validation is format-only. The portal validates allowed_domains against the tenant's registered domains. The API accepts any syntactically valid domain pattern without requiring the domain to be pre-registered. This allows Terraform workflows where Courier configuration is applied before domain registration.
Tenant limits are enforced. Creating a Courier when the tenant has reached its Courier limit returns 403 LIMIT_REACHED.
Create a Courier¶
POST /api/v1/service/couriers
Authorization: Bearer <token>
Content-Type: application/json
Request body:
{
"name": "Production Courier",
"description": "Pulls certs for prod k8s cluster",
"owner": "platform-team",
"contact_email": "[email protected]",
"status": "active",
"auth_method": "client_secret",
"allowed_domains": ["*.prod.example.com"]
}
Success response (201 Created):
{
"success": true,
"message": "Courier created successfully. Store the client_secret securely - it cannot be retrieved again.",
"data": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"name": "Production Courier",
"description": "Pulls certs for prod k8s cluster",
"owner": "platform-team",
"contact_email": "[email protected]",
"status": "active",
"auth_method": "client_secret",
"allowed_domains": ["*.prod.example.com"],
"client_id": "cour_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"last_seen_at": null,
"last_ip_address": null,
"request_count": 0,
"created_at": "2026-04-14T10:00:00+00:00",
"updated_at": "2026-04-14T10:00:00+00:00"
}
}
Subsequent GET responses for client_secret Couriers include client_id but omit client_secret.
Authentication Method Configurations¶
The auth_method field and its corresponding auth_config or client_certificate field determine how the Courier will authenticate at runtime.
client_secret — no auth_config required; credentials are auto-generated:
{ "auth_method": "client_secret" }
azure_jwt — for workloads on Azure with a managed identity or workload identity:
{
"auth_method": "azure_jwt",
"auth_config": {
"tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"allowed_audiences": "api://my-app"
}
}
azure_arc — for on-premises servers enrolled in Azure Arc:
{
"auth_method": "azure_arc",
"auth_config": {
"tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"allowed_resource_ids": "/subscriptions/*/resourceGroups/prod/*",
"require_tag_validation": true
}
}
oidc_oauth2 — for generic OIDC identity providers (including AWS IAM OIDC):
{
"auth_method": "oidc_oauth2",
"auth_config": {
"issuer": "https://accounts.google.com",
"client_id": "my-client-id",
"allowed_audiences": ""
}
}
spiffe_spire — for workloads with a SPIRE agent:
{
"auth_method": "spiffe_spire",
"auth_config": {
"trust_domain": "example.org",
"allowed_spiffe_ids": "spiffe://example.org/service/app,spiffe://example.org/service/worker"
}
}
pki_certificate — for X.509 client certificate authentication:
{
"auth_method": "pki_certificate",
"client_certificate": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
}
Only certificate_fingerprint (SHA-256) is returned in responses; the full certificate is not.
Error Codes¶
| Code | HTTP | Meaning |
|---|---|---|
COURIER_NOT_FOUND |
404 | Courier UUID not found in this tenant |
LIMIT_REACHED |
403 | Tenant Courier limit exceeded |
AUTH_METHOD_IMMUTABLE |
422 | Attempt to change auth_method after creation |
INVALID_CERTIFICATE |
422 | client_certificate is not a valid PEM X.509 certificate |
VALIDATION_ERROR |
422 | Invalid request body or missing required auth_config fields |
INSUFFICIENT_SCOPE |
403 | Write operation attempted with a read-only token |
INTERNAL_ERROR |
500 | Unexpected server error |