# Settlex Flow - Onboarding

> **Version:** 1.0.0\
> **Base URL:** `settlex-api.luganodes.com`\
> **Protocol:** HTTP/REST

***

## Authentication

All endpoints under **Party**, **API Key**, and **Balance** require a valid JWT token passed via the `Authorization` header using the **Bearer** scheme.

### Authorization Header Format

```http
Authorization: Bearer <jwt_token>
```

### Authentication Failure Responses

| Status Code | Response Body                    | Condition                                               |
| ----------- | -------------------------------- | ------------------------------------------------------- |
| `401`       | `{ "message": "Unauthorized" }`  | Missing or malformed `Authorization` header             |
| `401`       | `{ "message": "Invalid token" }` | Token is valid JWT but payload doesn't match schema     |
| `401`       | `{ "message": "Unauthorized" }`  | User not found in database or token verification failed |

***

## Error Handling

The API uses standard HTTP status codes and returns errors in a consistent JSON format:

```json
{
  "message": "Human-readable error description"
}
```

### Common Error Codes

| Status Code | Meaning               | Typical Cause                                          |
| ----------- | --------------------- | ------------------------------------------------------ |
| `400`       | Bad Request           | Request body validation failed (Zod schema errors)     |
| `401`       | Unauthorized          | Missing, invalid, or expired JWT token                 |
| `404`       | Not Found             | Requested resource does not exist                      |
| `409`       | Conflict              | Resource already exists (duplicate email, party, etc.) |
| `500`       | Internal Server Error | Unexpected server-side error or ledger failure         |
| `429`       | Rate Limit Exceeded   | A default rate limit of 300 requests every 15 minutes  |

### Validation Error Format

When request body validation fails, the error message contains field-level details joined by semicolons:

```json
{
  "message": "email: Invalid email; password: String must contain at least 8 character(s)"
}
```

***

## Endpoints

### Health

**`GET /api/health`**

A simple health check endpoint to verify the API server is running and responsive.

**Authentication:** None

**Request**

No request body or query parameters required.

**Response**

**200 OK**

The server is healthy and operational.

```json
{
  "message": "OK"
}
```

| Field     | Type   | Description                |
| --------- | ------ | -------------------------- |
| `message` | string | Health status confirmation |

**Example**

```bash
curl -X GET http://settlex.luganodes.com/api/health
```

```json
{
  "message": "OK"
}
```

***

### Onboarding

**`POST /api/onboarding/signup`**

Creates a new user account

**Authentication:** None

**Request Body**

| Field      | Type   | Required | Validation                    | Description                |
| ---------- | ------ | -------- | ----------------------------- | -------------------------- |
| `email`    | string | Yes      | Must be a valid email address | The user's email address   |
| `password` | string | Yes      | Minimum 8 characters          | The user's chosen password |

**Request Example**

```json
{
  "email": "user@example.com",
  "password": "securePassword123"
}
```

**Responses**

**201 Created**

User account was successfully created.

```json
{
  "message": "User created successfully",
  "email": "user@example.com"
}
```

| Field     | Type   | Description                                    |
| --------- | ------ | ---------------------------------------------- |
| `message` | string | Success confirmation message                   |
| `email`   | string | The email address of the newly created account |

**400 Bad Request**

The request body failed validation. The `message` field contains details about which fields failed and why.

```json
{
  "message": "email: Invalid email; password: String must contain at least 8 character(s)"
}
```

| Field     | Type   | Description                                   |
| --------- | ------ | --------------------------------------------- |
| `message` | string | Semicolon-separated list of validation errors |

**409 Conflict**

A user with the given email address already exists.

```json
{
  "message": "User with this email already exists"
}
```

*or*

```json
{
  "message": "Email already exists"
}
```

| Field     | Type   | Description          |
| --------- | ------ | -------------------- |
| `message` | string | Conflict description |

**500 Internal Server Error**

An unexpected error occurred during user creation.

```json
{
  "message": "Internal server error"
}
```

**Example**

```bash
curl -X POST http://settlex.luganodes.com/api/onboarding/signup \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "password": "myStrongPass99"
  }'
```

**`POST /api/onboarding/login`**

Authenticates a user with email and password, returning a JWT token on success.

**Authentication:** None

**Request Body**

| Field      | Type   | Required | Validation                    | Description              |
| ---------- | ------ | -------- | ----------------------------- | ------------------------ |
| `email`    | string | Yes      | Must be a valid email address | The user's email address |
| `password` | string | Yes      | Minimum 8 characters          | The user's password      |

**Request Example**

```json
{
  "email": "user@example.com",
  "password": "securePassword123"
}
```

**Responses**

**200 OK**

Authentication successful. Returns a JWT token valid for 7 days, the user's email, and optionally their Canton party ID (if one has been created).

```json
{
  "message": "Login successful",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "email": "user@example.com",
  "partyId": "party-abc123::1220abcd..."
}
```

| Field     | Type                | Description                                                           |
| --------- | ------------------- | --------------------------------------------------------------------- |
| `message` | string              | Success confirmation message                                          |
| `token`   | string              | JWT token for authenticating subsequent API requests                  |
| `email`   | string              | The authenticated user's email address                                |
| `partyId` | string \| undefined | The user's Canton ledger party ID (undefined if no party created yet) |

**400 Bad Request**

The request body failed validation.

```json
{
  "message": "email: Invalid email"
}
```

| Field     | Type   | Description                                   |
| --------- | ------ | --------------------------------------------- |
| `message` | string | Semicolon-separated list of validation errors |

**401 Unauthorized**

The email does not match any registered user, or the password is incorrect. The same message is returned for both cases to prevent user enumeration.

```json
{
  "message": "Invalid email or password"
}
```

| Field     | Type   | Description                    |
| --------- | ------ | ------------------------------ |
| `message` | string | Generic authentication failure |

**500 Internal Server Error**

An unexpected error occurred during authentication.

```json
{
  "message": "Internal server error"
}
```

**Example**

```bash
curl -X POST http://settlex.luganodes.com/api/onboarding/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "password": "myStrongPass99"
  }'
```

**Example Response**

```json
{
  "message": "Login successful",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2ODI1YzRhNmE4OGU1NTAwMTIzNDU2NzgiLCJlbWFpbCI6ImFsaWNlQGV4YW1wbGUuY29tIiwiaWF0IjoxNzE3MzEyMDAwLCJleHAiOjE3MTc5MTY4MDB9.abc123",
  "email": "alice@example.com",
  "partyId": null
}
```

***

### Party

**`POST /api/party/create`**

#### **NOTE: This endpoint will take \~10 seconds to fulfill as it involves several ledger interactions sequentially.**

Allocates a new internal party on the Canton Network ledger for the authenticated user. Each user can only have one party.

After successful allocation, the endpoint:

{% stepper %}
{% step %}

#### Grant ledger rights (`readAs` and `actAs`) to Settlex

If rights granting fails, the party assignment is rolled back to maintain consistency between the database and the ledger.
{% endstep %}

{% step %}

#### Creates a transfer preapproval contract on the ledger for your party.

Builds a transfer preapproval command (referencing the configured provider party and DSO party), and submits it. If preapproval creation fails, the endpoint returns `500` with the error details.
{% endstep %}
{% endstepper %}

**Authentication:** Required (Bearer Token)

**Request Body**

No request body required. The user is identified from the JWT token.

**Responses**

**200 OK**

Party was successfully allocated on the ledger, assigned to the user, granted rights, and a transfer preapproval contract was created.

```json
{
    "message": "Party created successfully",
    "partyId": "9a923fe2-35a8-45f5-bccc-24848a393004::12207b916c3752acac9902d845ee0340d3b66e8ce0d1e2a00b187d97b99b10c44529",
    "rightsGranted": {
        "newlyGrantedRights": [
            {
                "kind": {
                    "CanReadAs": {
                        "value": {
                            "party": "9a923fe2-35a8-45f5-bccc-24848a393004::12207b916c3752acac9902d845ee0340d3b66e8ce0d1e2a00b187d97b99b10c44529"
                        }
                    }
                }
            },
            {
                "kind": {
                    "CanActAs": {
                        "value": {
                            "party": "9a923fe2-35a8-45f5-bccc-24848a393004::12207b916c3752acac9902d845ee0340d3b66e8ce0d1e2a00b187d97b99b10c44529"
                        }
                    }
                }
            }
        ]
    },
    "transferPreapproval": {
        "updateId": "122074aa25376a3d5e7d4e70cc93d603625ba46582eef0e081f3fa45620d8dc2a1ca",
        "completionOffset": 1117206
    }
}
```

| Field                 | Type   | Description                                                                                        |
| --------------------- | ------ | -------------------------------------------------------------------------------------------------- |
| `message`             | string | Success confirmation message                                                                       |
| `partyId`             | string | The Canton Network party identifier allocated to the user                                          |
| `rightsGranted`       | object | The ledger rights granted to the party (structure from Canton SDK)                                 |
| `transferPreapproval` | object | The result of submitting the transfer preapproval command (contains completionOffset and updateId) |

**400 Bad Request**

Validation error.

```json
{
  "message": "field: error description"
}
```

**401 Unauthorized**

Missing or invalid authentication token. See [Authentication Failure Responses](#authentication-failure-responses).

**409 Conflict**

The user already has a party assigned. This is checked both at the start and atomically during the update.

```json
{
  "message": "Party already exists"
}
```

| Field     | Type   | Description                                          |
| --------- | ------ | ---------------------------------------------------- |
| `message` | string | Indicates the user already has a party on the ledger |

**500 Internal Server Error**

Ledger allocation, rights granting, transfer preapproval creation, or an unexpected error occurred.

```json
{
  "message": "Failed to allocate party"
}
```

*or:*

```json
{
  "message": "Failed to grant ledger rights"
}
```

*or (transfer preapproval failures):*

```json
{
  "message": "Failed to build transfer preapproval contract command"
}
```

```json
{
  "message": "Failed to submit transfer preapproval contract command"
}
```

```json
{
  "message": "Failed to create transfer preapproval contract for party"
}
```

*or:*

```json
{
  "message": "Internal server error"
}
```

| Field     | Type   | Description                            |
| --------- | ------ | -------------------------------------- |
| `message` | string | Description of the server-side failure |

**Example**

```bash
curl -X POST http://settlex.luganodes.com/api/party/create \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
```

**Example Response**

```json
{
  "message": "Party created successfully",
  "partyId": "party-abc123::1220abcdef0123456789abcdef0123456789",
  "rightsGranted": {
    "readAs": ["party-abc123::1220abcdef0123456789abcdef0123456789"],
    "actAs": ["party-abc123::1220abcdef0123456789abcdef0123456789"]
  },
  "transferPreapproval": {}
}
```

***

### API Key

**`POST /api/api-key/create`**

Generates a new API key for the authenticated user. The raw key is generated using `crypto.randomBytes(32)` (64 hex characters) and is returned to the user **only once**. Each user can only have one active API key. If the user previously deactivated their key (via `DELETE /api/api-key`), calling this endpoint will reactivate with a freshly generated key.

**Authentication:** Required (Bearer Token)

**Request Body**

No request body required.

**Responses**

**200 OK**

API key was successfully generated and stored.

```json
{
  "message": "Api key created successfully",
  "apiKeyRaw": "a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890"
}
```

| Field       | Type   | Description                                                                              |
| ----------- | ------ | ---------------------------------------------------------------------------------------- |
| `message`   | string | Success confirmation message                                                             |
| `apiKeyRaw` | string | The raw API key (64 hex characters). Store this securely — it cannot be retrieved again. |

> **Important:** The `apiKeyRaw` value is shown only once upon creation. We store only the key hash. If lost, a new key must be generated (after deleting the existing one, if applicable).

**401 Unauthorized**

Missing or invalid authentication token. See [Authentication Failure Responses](#authentication-failure-responses).

**409 Conflict**

The user already has an **active** API key. Only one active API key is allowed per user. Delete the existing key first before creating a new one.

```json
{
  "message": "Api key already exists"
}
```

| Field     | Type   | Description                                 |
| --------- | ------ | ------------------------------------------- |
| `message` | string | Indicates a duplicate API key for this user |

**500 Internal Server Error**

An unexpected error occurred during key generation or storage.

```json
{
  "message": "Internal server error"
}
```

**Example**

```bash
curl -X POST http://settlex.luganodes.com/api/api-key/create \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
```

**Example Response**

```json
{
  "message": "Api key created successfully",
  "apiKeyRaw": "f47ac10b58cc4372a5670e02b2c3d479f47ac10b58cc4372a5670e02b2c3d479"
}
```

**`DELETE /api/api-key`**

Deactivates the authenticated user's API key. After deactivation, the user can generate a new key via `POST /api/api-key/create`.

**Authentication:** Required (Bearer Token)

**Request Body**

No request body required. The user is identified from the JWT token.

**Responses**

**200 OK**

The API key was successfully deactivated.

```json
{
  "message": "Api key deleted successfully"
}
```

| Field     | Type   | Description                  |
| --------- | ------ | ---------------------------- |
| `message` | string | Success confirmation message |

**401 Unauthorized**

Missing or invalid authentication token. See [Authentication Failure Responses](#authentication-failure-responses).

**404 Not Found**

No active API key was found for the authenticated user. This occurs if the user never created a key or already deactivated it.

```json
{
  "message": "Api key not found"
}
```

| Field     | Type   | Description                                             |
| --------- | ------ | ------------------------------------------------------- |
| `message` | string | Indicates no active API key exists for the current user |

**500 Internal Server Error**

An unexpected error occurred during key deactivation.

```json
{
  "message": "Internal server error"
}
```

**Example**

```bash
curl -X DELETE http://settlex.luganodes.com/api/api-key \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
```

**Example Response**

```json
{
  "message": "Api key deleted successfully"
}
```

***

### Balance

**`GET /api/balance`**

Retrieves the available and reserved balance for the authenticated user's Canton party.

**Authentication:** Required (Bearer Token)

**Request**

No request body or query parameters required. The party is identified from the authenticated user's `partyId`.

**Responses**

**200 OK**

Balance successfully retrieved.

```json
{
  "balance": 1000.50,
  "reservedBalance": 250.00
}
```

| Field             | Type   | Description                                                                  |
| ----------------- | ------ | ---------------------------------------------------------------------------- |
| `balance`         | number | The available (spendable) balance for the user's party in Canton Coin (CC)   |
| `reservedBalance` | number | The amount currently reserved (locked for pending operations) in Canton Coin |

**401 Unauthorized**

Missing or invalid authentication token. See [Authentication Failure Responses](#authentication-failure-responses).

**404 Not Found**

The user's party was not found in the database. This can happen if the user has not yet created a party, or if the party record does not exist in the database.

```json
{
  "message": "Party not found"
}
```

| Field     | Type   | Description                                    |
| --------- | ------ | ---------------------------------------------- |
| `message` | string | Indicates no party record exists for this user |

**500 Internal Server Error**

An unexpected error occurred while querying the balance.

```json
{
  "message": "Internal server error"
}
```

**Example**

```bash
curl -X GET http://settlex.luganodes.com/api/balance \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
```

**Example Response**

```json
{
  "balance": 5000,
  "reservedBalance": 150.75
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.luganodes.com/apis/settlex-flow/settlex-flow-onboarding.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
