API reference › Accounts › Sign up
Start a signup
/v1/signuppropgate signupThe only unauthenticated route that writes to the database, besides its own
confirm step — there is no key to present yet, so the mailbox is the
credential. Sends a six-digit code to the address given, valid for ten
minutes and single-use.
| Field | Type | Description |
|---|---|---|
| email* | string | The address to send the code to. Lowercased and trimmed before storage, so `A@b.com` and `a@b.com` are one account. |
curl -s -X POST https://api.propgate.dev/v1/signup \
-H "content-type: application/json" \
-d '{"email":"you@example.com"}'{
"data": {
"object": "signup",
"status": "pending"
},
"error": null,
"meta": null
}The response is identical whether or not the address already has an account. That reads like a missing feature until you know why: a signup endpoint that answers already registered tells whoever holds a leaked address list which of those addresses use us. Running this again on an address that already has one mints an additional key against the same account rather than a second account — that is also the recovery path if you lose a key, which is why there is no separate sign-in.
Next, exchange the code with POST /v1/signup/confirm.
Failure modes
A malformed body is a 422, in the same field: message shape used
throughout the API:
{
"data": null,
"error": {
"message": "email: Invalid input: expected string, received undefined"
},
"meta": null
}{
"data": null,
"error": {
"message": "email: Too small: expected string to have >=3 characters"
},
"meta": null
}Signup and confirm share a limiter of twenty requests per client address per
hour — the tripwire against turning this into a spam relay against our own
sending reputation. A 429 names the wait and sets Retry-After:
{
"data": null,
"error": {
"message": "too many signup requests; try again in 2117s"
},
"meta": null
}