Create an Admin Portal link
POST /v1/admin-portal-links · permission admin-portal-links:write
Creates a link that lets the IT administrator of one of your customer organizations set up SSO, directory sync, domain verification and more in the hosted Admin Portal, without your support team. Generate a fresh link each time your administrator clicks "Set up SSO" in your application and redirect them to url right away: the link is valid for 5 minutes and opens one portal session exactly once. url is returned only in this response, once; no endpoint returns it later. The response carries Cache-Control: no-store, and a response replayed for the same Idempotency-Key omits url and carries secret_redacted: true. The portal session lasts 60 minutes idle, 8 hours at most; revoke it with DELETE /v1/admin-portal-links/{link_id}. The creation is audited as admin_portal_link.created (the URL never enters the audit log).
audit_logs needs the Business plan and log_streams the Enterprise plan; the other intents are available on every plan. delivery: email sends an invitation instead: it lives 7 days and is mailed to recipient_email; the response carries no url and no address. When the recipient opens it, a 6-digit code (valid 10 minutes) goes to the same address, and only the verified code opens the portal session. If the invitation mail cannot be sent, the call answers 503 service_unavailable with Retry-After and creates nothing. Revoke an invitation with DELETE /v1/admin-portal-links/{link_id}. The recipient address never appears in a response, a list, the audit log or a log.
Request
| Field | Type | Required | Description |
|---|---|---|---|
organization_id |
string | yes | Organization ID (org_…); it must exist in this environment. |
intents |
string[] | yes | 1–6 of sso, dsync, domain_verification, certificate_renewal, audit_logs (Business), log_streams (Enterprise), none twice. |
return_url |
string | yes | Absolute https URL of at most 2048 bytes, without user information or fragment. Its origin must be the origin of a redirect_uri of an active application registered in this environment. |
delivery |
string | no | link (default; 5-minute one-time url in the response) or email (7-day invitation mailed to recipient_email; no url). |
recipient_email |
string | no | Only with delivery: email, and required there; at most 320 characters. Missing: 422 /recipient_email required; with delivery: link: not_allowed; not a valid address: invalid_value. |
Response
201 Created with the link and its one-time url.
{
"id": "apl_01j9xv2c4d6e8f0g2h4j6k8m0n",
"organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
"intents": [
"dsync",
"sso"
],
"delivery": "link",
"return_url": "https://app.example.com/settings/sso",
"expires_at": "2026-10-12T09:36:00.123456Z",
"consumed_at": null,
"revoked_at": null,
"created_at": "2026-10-12T09:31:00.123456Z",
"url": "https://acme.eu.keenauth.app/portal/l/<token>"
}
Errors
| HTTP | code | When |
|---|---|---|
| 403 | plan_feature_unavailable |
An intent needs a higher plan; feature and required_plan name it. |
| 404 | not_found |
The organization does not exist in this environment. |
| 409 | organization_disabled |
The organization is not active (disabled); no link is created. |
| 409 | organization_pending_deletion |
The organization is scheduled for deletion; no link is created. |
| 422 | validation_failed |
A field is missing or invalid; /return_url origin_not_registered when its origin is not a registered application origin; /recipient_email required, not_allowed or invalid_value. |
| 401 | unauthenticated |
The credential is missing, invalid or revoked. |
| 403 | insufficient_permissions |
The credential lacks the required permission. |
| 429 | rate_limited |
The rate limit was exceeded; retry after the Retry-After period. |
| 503 | service_unavailable |
A dependency is temporarily unavailable, or (delivery: email) the invitation mail cannot be sent; nothing is created. Retry after the Retry-After period. |
Example
curl -i -X POST "https://api.eu.keenauth.com/v1/admin-portal-links" \
-H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"organization_id":"org_01j9xk3m7q8r2s4t6v8w0y2z4a","intents":["sso","dsync"],"return_url":"https://app.example.com/settings/sso"}'
Send an invitation by e-mail
curl -i -X POST "https://api.eu.keenauth.com/v1/admin-portal-links" \
-H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"organization_id":"org_01j9xk3m7q8r2s4t6v8w0y2z4a","intents":["sso"],"return_url":"https://app.example.com/settings/sso","delivery":"email","recipient_email":"it@customer.example"}'
201 Created; there is no url and no address in the response:
{
"id": "apl_01j9xv2c4d6e8f0g2h4j6k8m0n",
"organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
"intents": [
"sso"
],
"delivery": "email",
"return_url": "https://app.example.com/settings/sso",
"expires_at": "2026-10-19T09:31:00.123456Z",
"consumed_at": null,
"revoked_at": null,
"created_at": "2026-10-12T09:31:00.123456Z"
}
With the CLI: keenauth admin-portal-links create --organization-id org_01j9xk3m7q8r2s4t6v8w0y2z4a --intents sso --return-url https://app.example.com/settings/sso --delivery email --recipient-email it@customer.example.