hosting

POST /v1/sites/{siteId}/wp-login

Mint a one-click WordPress admin login token.

All hosting endpoints

All developer docs →

Authentication

Send an API key as a bearer token. The key must carry the sites.wp_login permission; a key without it is refused with 403, not 404.

This endpoint takes no organisation id. Your key already identifies the organisation it belongs to, and the response is scoped to it.

Try it

Replace anything in angle brackets with your own values, and the key placeholder with a key from your dashboard.

curl -X POST https://api.zinndigital.com/v1/sites/{siteId}/wp-login \
  -H "Authorization: Bearer zdk_live_…" \
  -H "Content-Type: application/json" \
  -d '{  }'

Signed in? The API console in your dashboard fills in your real organisation id and your own key, and runs the request against the live API so you can see the actual response. Open this endpoint in the API console

Details

Returns a short-lived, signed SSO token (and the URL that carries it) for one-click sign-in to the site's wp-admin (V1 parity). The customer's browser is sent to the URL; the Zinn® plugin on the site verifies the token, enforces single-use, and establishes the wp-admin session. The token is bound to this site, expires in ~2 minutes, and its issuance is audit-logged with the real actor. Requires sites.wp_login. Only WordPress/WooCommerce sites that are serving are eligible (422 / 409 otherwise); if one-click login is not configured on the platform the endpoint returns 503 and mints nothing. ⛔⛔ A 201 from this endpoint is a claim about the SITE, not merely about the signature, and it did not used to be. Minting is local, cheap and always succeeds; every reason a grant cannot work lives on the box. Before the preflight below existed this endpoint answered 201 with a correctly-signed URL for a site whose WordPress had no SSO key, no plugin to serve the route, and — measured on 2026-08-15 — was returning HTTP 500 to every request. Three such grants were issued and audit-logged in one day, each of which opened an error page. So before signing anything the endpoint now checks, and repairs what it can: the hosting platform can receive an SSO key at all — this platform exposes no shell and no wp-cli, so its sites can never honour one and are refused 422 (PLATFORM_CANNOT_SSO) rather than handed a token; the plugin that serves the route is installed and active — installed automatically if it is not, because a precondition the platform can satisfy itself is not one the customer should read about; * the site's ZINN_SSO_KEY is present on the box, asked of the file rather than of our own record of having written it — the two disagreed; and the site actually answers HTTP without a server error, probed from the box against the origin so a CDN cache cannot report health the site no longer has. A site that fails this is 409 (SITE_NOT_REACHABLE). the site's domain actually serves THIS site: while a partner move into it is not yet live (the domain still opens the original copy) the answer is 409 with details[].code move_in_progress, and while an import into it is unfinished, import_in_progress — never a login URL that would open someone else's server. The error body's reason carries which of those failed, so a client can say the true thing instead of "something went wrong".

Parameters

NameTypeRequiredWhat it is
siteId (path)UuidYesSite ID (UUIDv7).

Request body

NameTypeRequiredWhat it is
wp_usernamestringNoThe WordPress user to sign in as. Omit or leave blank for the site's primary administrator (the plugin resolves it).

Response

NameTypeRequiredWhat it is
urlstringYesThe site URL carrying the token — send the customer's browser here to complete one-click login, unchanged. The token is single-use and short-lived. When the site's plugin has the…
tokenstringYesThe signed SSO token (also embedded in url). Never put it in a query string yourself — follow url as given.
wp_usernamestringYesThe target WordPress user ("" = the site's primary administrator).
expires_atstringYesWhen the token expires (UTC).

Errors this endpoint can return

401 · 403 · 404 · 409 · 422 · 429 · 503