Commerce

Finish connecting a Drupal site with the emailed code

POST
/v1/drupal/connect/code

Signed with the site key, like connect/verify. The code lives at most 300 seconds and allows three attempts: a wrong code answers 400 DRUPAL_CODE_INVALID, and once three have failed, 410 DRUPAL_CODE_EXHAUSTED. A correct code is claimed once and connects the site, with the same answer and the same binding as connect/verify.

An emailed code proves the billing inbox, not the site: it may connect a new site, never take over a connected one (409 DRUPAL_SITE_ALREADY_CONNECTED). It answers 503 DRUPAL_EMAIL_CODE_UNAVAILABLE while codes cannot be checked.

Request Body

application/json

protocol*string
purpose*string
connect_id*string
Formatuuid
code*string
Match^[0-9]{6}$
signature*DrupalSiteSignature

The site key's Ed25519 signature, base64url without padding, over the key-sorted canonical JSON of the body without signature.

Match^[A-Za-z0-9_-]{86}$

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/drupal/connect/code" \  -H "Content-Type: application/json" \  -d '{    "protocol": "decionis-drupal-connect/1",    "purpose": "code",    "connect_id": "a1b4c748-916a-48f3-ac78-f681f542c9c8",    "code": "string",    "signature": "string"  }'
{  "status": "connected",  "org_id": "a40f5d1f-d889-42e9-94ea-b9b33585fc6b",  "api_key": "string",  "mode": "shadow",  "entitlement": null}

Prove a Drupal site by its challenge, or have a code emailed POST

Signed with the site key: `signature` is the Ed25519 signature, in base64url, over the key-sorted canonical JSON of the body without `signature`. For a reachable site, Decionis fetches `https://<site>/.well-known/decionis-challenge` over HTTPS with no redirects, a five-second budget and a 16 KB cap. The site answers a JSON object with `protocol` (`decionis-drupal-connect/1`), `purpose` (`challenge`), `connect_id`, `site_url`, `challenge`, and `signature`: the site key's signature over the rest, signed the same way. A match claims the challenge, once, and connects the site (200). A site that answers without a valid proof is refused (403 DRUPAL_CHALLENGE_FAILED) and is not offered a code. For a site Decionis cannot reach, or that gives no answer at all, a 6-digit code is emailed to the billing address instead (202); finish with `connect/code`. Until a code sender is configured, that answers 503 DRUPAL_EMAIL_CODE_UNAVAILABLE. Connecting binds the site to one Decionis organization for good, creating it the first time, and answers that organization's least-privilege key (role MEMBER, scopes decision:read and decision:write) exactly once: only its hash is kept. Proving a connected site again issues a new key and revokes the one it had. The site starts in Shadow. The challenge is single-use. A second verify of a used challenge answers 409 DRUPAL_CHALLENGE_ALREADY_USED, and a connect that has connected, failed or sent a code answers 409 DRUPAL_CONNECT_NOT_PENDING: start a new one. When Decionis refuses to bind the site, the answer carries that status with UPSTREAM_UNAVAILABLE.

Read a connected Drupal site's mode POST

Authenticated with the key the site received when it connected. Every Drupal site runs in Shadow today and `entitlement` is always null: no enforcement entitlement exists for Drupal workspaces yet. An unknown organization and a wrong key get the same 401. Read-only; safe to repeat.