# Sign-up

> Public registration, invitations and onboarding.

Public registration, invitations and onboarding.

| Operation | Method and path |
| --- | --- |
| [Get public sign-up configuration](#get-public-sign-up-configuration) | `GET /v1/signups/config` |
| [Get a proof-of-work challenge](#get-a-proof-of-work-challenge) | `GET /v1/signups/challenge` |
| [Join the waitlist](#join-the-waitlist) | `POST /v1/waitlist` |
| [Confirm a waitlist email address](#confirm-a-waitlist-email-address) | `POST /v1/waitlist/confirm` |
| [Preview an invitation](#preview-an-invitation) | `GET /v1/invites/{code}` |
| [Accept an invitation and start sign-up](#accept-an-invitation-and-start-sign-up) | `POST /v1/invites/{code}/accept` |
| [Begin public sign-up](#begin-public-sign-up) | `POST /v1/signups` |
| [Get onboarding progress](#get-onboarding-progress) | `GET /v1/me/onboarding` |
| [Update onboarding progress](#update-onboarding-progress) | `PATCH /v1/me/onboarding` |

## Get public sign-up configuration

```http
GET /v1/signups/config
```

Returns the current access mode, legal versions, consent copy, billing state and available plans.

- **Operation:** `getSignupConfig`
- **Scopes:** none (public)

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Sign-up configuration. | [SignupConfig](/docs/api/objects/signup-config/) |

**Errors:** `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/signups/config \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "mode": "string",
  "waitlist_open": false,
  "invite_required": false,
  "data_location": "string",
  "terms": {
    "version": "string",
    "url": "string"
  },
  "privacy": {
    "version": "string",
    "url": "string"
  },
  "waitlist_consent": {
    "version": "string",
    "text": "string"
  },
  "billing_enabled": false,
  "proof_of_work": {
    "difficulty_bits": 1
  },
  "plans": [
    {
      "id": "string",
      "name": "string",
      "price": {
        "amount": "7.42",
        "currency": "GBP",
        "display": "£7.42"
      },
      "card_required": false
    }
  ]
}
```

## Get a proof-of-work challenge

```http
GET /v1/signups/challenge
```

Solve SHA-256(challenge + ":" + decimal nonce) until the digest has at least difficulty_bits leading zero
bits. The signed challenge expires after ten minutes and each successful solution can be used once.

- **Operation:** `getSignupChallenge`
- **Scopes:** none (public)

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Proof-of-work challenge. | [SignupChallenge](/docs/api/objects/signup-challenge/) |

**Errors:** `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/signups/challenge \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "challenge": "string",
  "algorithm": "string",
  "difficulty_bits": 1,
  "expires_at": "2026-10-22T13:05:00Z"
}
```

## Join the waitlist

```http
POST /v1/waitlist
```

Sends a double opt-in confirmation email. The response is identical for new and existing entries.

- **Operation:** `joinWaitlist`
- **Scopes:** none (public)
- **Idempotency-Key:** optional

**Request body** (`application/json`, required): [WaitlistJoinRequest](/docs/api/objects/waitlist-join-request/)

| Field | Type | Description |
| --- | --- | --- |
| `email` * | string (email) |  |
| `name` | string |  |
| `building` | string |  |
| `heard_from` | string |  |
| `consent` * | boolean |  |
| `consent_version` * | string |  |
| `website` | string |  |
| `pow` | [SignupPow](/docs/api/objects/signup-pow/) |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `202` Accepted | Confirmation email queued. | [WaitlistJoinAccepted](/docs/api/objects/waitlist-join-accepted/) |

**Errors:** `400`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/waitlist \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "ada@example.co.uk",
  "name": "string",
  "building": "string",
  "heard_from": "string",
  "consent": false,
  "consent_version": "string",
  "website": "string",
  "pow": {
    "challenge": "string",
    "nonce": "string"
  }
}'
```

**Example response**

```json
{
  "status": "pending_confirmation",
  "message": "string"
}
```

## Confirm a waitlist email address

```http
POST /v1/waitlist/confirm
```

Confirms the address using the single-use token sent by email. Repeating a valid confirmation is safe.

- **Operation:** `confirmWaitlist`
- **Scopes:** none (public)
- **Idempotency-Key:** optional

**Request body** (`application/json`, required): [WaitlistConfirmRequest](/docs/api/objects/waitlist-confirm-request/)

| Field | Type | Description |
| --- | --- | --- |
| `token` * | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Address confirmed. | [WaitlistConfirmed](/docs/api/objects/waitlist-confirmed/) |

**Errors:** `400`, `410`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/waitlist/confirm \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "token": "string"
}'
```

**Example response**

```json
{
  "status": "confirmed"
}
```

## Preview an invitation

```http
GET /v1/invites/{code}
```

Checks the invitation code without revealing the bound email address.

- **Operation:** `getInvite`
- **Scopes:** none (public)

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `code` * | path | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Invitation preview. | [InvitePreview](/docs/api/objects/invite-preview/) |

**Errors:** `403`, `404`, `409`, `410`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/invites/code \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "status": "valid",
  "email_hint": "string",
  "expires_at": "2026-10-22T13:05:00Z"
}
```

## Accept an invitation and start sign-up

```http
POST /v1/invites/{code}/accept
```

Starts email verification using the invitation code as the abuse barrier.

- **Operation:** `acceptInvite`
- **Scopes:** none (public)
- **Idempotency-Key:** optional

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `code` * | path | string |  |

**Request body** (`application/json`, required): [InviteAcceptRequest](/docs/api/objects/invite-accept-request/)

| Field | Type | Description |
| --- | --- | --- |
| `email` * | string (email) |  |
| `name` | string |  |
| `workspace_name` | string |  |
| `accept_terms` * | boolean |  |
| `terms_version` * | string |  |
| `privacy_version` * | string |  |
| `website` | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `202` Accepted | Email verification started. | [SignupStarted](/docs/api/objects/signup-started/) |

**Errors:** `400`, `403`, `404`, `409`, `410`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/invites/code/accept \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "ada@example.co.uk",
  "name": "string",
  "workspace_name": "string",
  "accept_terms": false,
  "terms_version": "string",
  "privacy_version": "string",
  "website": "string"
}'
```

**Example response**

```json
{
  "signup_id": "string",
  "verification_id": "string",
  "email": "string",
  "expires_at": "2026-10-22T13:05:00Z",
  "next": "verify_email"
}
```

## Begin public sign-up

```http
POST /v1/signups
```

Checks legal acceptance, email, invite mode and proof of work, then sends the existing email verification
code. For proof of work solve SHA-256(challenge + ":" + decimal nonce) for the configured leading zero bits.

- **Operation:** `beginSignup`
- **Scopes:** none (public)
- **Idempotency-Key:** optional

**Request body** (`application/json`, required): [SignupBeginRequest](/docs/api/objects/signup-begin-request/)

| Field | Type | Description |
| --- | --- | --- |
| `email` * | string (email) |  |
| `name` | string |  |
| `workspace_name` | string |  |
| `invite_code` | string |  |
| `accept_terms` * | boolean |  |
| `terms_version` * | string |  |
| `privacy_version` * | string |  |
| `marketing_opt_in` | boolean |  |
| `website` | string |  |
| `pow` | [SignupPow](/docs/api/objects/signup-pow/) |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `202` Accepted | Email verification started. | [SignupStarted](/docs/api/objects/signup-started/) |

**Errors:** `400`, `403`, `404`, `409`, `410`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/signups \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "ada@example.co.uk",
  "name": "string",
  "workspace_name": "string",
  "invite_code": "string",
  "accept_terms": false,
  "terms_version": "string",
  "privacy_version": "string",
  "marketing_opt_in": false,
  "website": "string",
  "pow": {
    "challenge": "string",
    "nonce": "string"
  }
}'
```

**Example response**

```json
{
  "signup_id": "string",
  "verification_id": "string",
  "email": "string",
  "expires_at": "2026-10-22T13:05:00Z",
  "next": "verify_email"
}
```

## Get onboarding progress

```http
GET /v1/me/onboarding
```

Returns plan, legal acceptance and checklist progress for the signed-in human user.

- **Operation:** `getMyOnboarding`
- **Scopes:** any signed-in principal

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Onboarding state. | [Onboarding](/docs/api/objects/onboarding/) |

**Errors:** `401`, `403`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/me/onboarding \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "workspace_id": "string",
  "plan": "trial",
  "plan_chosen": false,
  "trial_ends_at": "2026-10-22T13:05:00Z",
  "card": {
    "required": false,
    "status": "not_required",
    "setup_url": "https://guv.dev/"
  },
  "legal": {
    "terms_version": "string",
    "privacy_version": "string",
    "accepted_terms_version": "string",
    "accepted_privacy_version": "string",
    "needs_acceptance": false
  },
  "checklist": [
    {
      "id": "create_project",
      "title": "string",
      "description": "string",
      "status": "pending",
      "source": "auto",
      "completed_at": "2026-10-22T13:05:00Z"
    }
  ],
  "completed_count": 1,
  "total_count": 1,
  "complete": false,
  "dismissed": false,
  "version": 1
}
```

## Update onboarding progress

```http
PATCH /v1/me/onboarding
```

Choose a plan, accept current legal versions, dismiss onboarding or update manual checklist steps.

- **Operation:** `updateMyOnboarding`
- **Scopes:** any signed-in principal
- **Idempotency-Key:** required
- **Concurrency:** send the `ETag` you read as `If-Match`

**Request body** (`application/json`, required): [OnboardingUpdate](/docs/api/objects/onboarding-update/)

| Field | Type | Description |
| --- | --- | --- |
| `plan` | `trial`, `hobby`, `pro`, `business`, `dedicated` |  |
| `accept_legal` | object |  |
| `dismissed` | boolean |  |
| `steps` | array of object |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Updated onboarding state. | [Onboarding](/docs/api/objects/onboarding/) |

**Errors:** `400`, `401`, `403`, `409`, `412`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X PATCH https://api.guv.dev/v1/me/onboarding \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "plan": "trial",
  "accept_legal": {
    "terms_version": "string",
    "privacy_version": "string"
  },
  "dismissed": false,
  "steps": [
    {
      "id": "create_project",
      "status": "complete"
    }
  ]
}'
```

**Example response**

```json
{
  "workspace_id": "string",
  "plan": "trial",
  "plan_chosen": false,
  "trial_ends_at": "2026-10-22T13:05:00Z",
  "card": {
    "required": false,
    "status": "not_required",
    "setup_url": "https://guv.dev/"
  },
  "legal": {
    "terms_version": "string",
    "privacy_version": "string",
    "accepted_terms_version": "string",
    "accepted_privacy_version": "string",
    "needs_acceptance": false
  },
  "checklist": [
    {
      "id": "create_project",
      "title": "string",
      "description": "string",
      "status": "pending",
      "source": "auto",
      "completed_at": "2026-10-22T13:05:00Z"
    }
  ],
  "completed_count": 1,
  "total_count": 1,
  "complete": false,
  "dismissed": false,
  "version": 1
}
```
