API reference › Accounts › Confirm
Confirm a signup
/v1/signup/confirmpropgate confirmExchanges the code from POST /v1/signup for an
account and a key. The code is single-use: a second confirm with it is a
409, not a second key.
| Field | Type | Description |
|---|---|---|
| email* | string | The same address `signup` was called with. |
| code* | string | The six-digit code sent to that address. Valid for ten minutes. |
curl -s -X POST https://api.propgate.dev/v1/signup/confirm \
-H "content-type: application/json" \
-d '{"email":"you@example.com","code":"123456"}'{
"data": {
"apiKey": "pg_live_...",
"created": true,
"object": "account",
"tenantId": "019fcf4f-..."
},
"error": null,
"meta": null
}apiKey is readable exactly once, here. Only its hash is stored, so no
endpoint can ever show it again, not this one on a retry and not a future one.
Losing it means running signup again, which mints an additional key against
the same account rather than a second one.
created distinguishes a brand-new account from a code confirmed on an
address that already had one. Both mint a key, but only one is a new tenant.
Failure modes
A malformed body, whether a code that isn't six characters or a missing
field, is a 422 in the same field: message shape used throughout the API:
{
"data": null,
"error": {
"message": "code: Too small: expected string to have >=6 characters"
},
"meta": null
}Everything else about "this code doesn't work" collapses into one 409,
regardless of which of four reasons produced it:
{
"data": null,
"error": {
"message": "that code is not valid or has already been used; request a new one with POST /v1/signup"
},
"meta": null
}A wrong code, an expired one, one that has already hit its attempt cap, and
one that was never issued all answer identically. The route can tell these
apart internally, but that distinction exists for the log rather than for the
client: "expired" would confirm the address signed up here, and "exhausted"
would confirm someone is actively guessing at it. Either one leaking to a
caller turns this endpoint into an oracle for the exact thing POST /v1/signup's identical response is designed to hide.
It is a 409 rather than a 422 because the common case by a wide margin is
a code that was already spent, from a double-submitted form or a second click,
which is a conflict with state rather than a malformed request. The message
says what to do next: call signup again.