# Manage Customers

Register, update, search, and authenticate customers for your brand. This section covers the full lifecycle of customer management, from account creation to login.

### Create Customer

Register a new customer for a specific brand. Depending on your configuration, you can use the **Standard Mode** (Legacy) or the **Hierarchy Mode** (Enterprise).

* * *

### 1\. Standard Creation (No Hierarchy)

In this mode, fields like \`class\`, \`section\`, and \`pricingRuleCode\` are free text inputs.

### `POST /brands/{brandId}/customers` - Create Customer

Create a new customer account without hierarchy constraints.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| brandId | integer | Yes | ID of the brand |

#### Request Body

```json
{
  "role": "customer",
  "brandId": 22,
  "badgeNumber": 10390970,
  "firstName": "First",
  "lastName": "Last",
  "phone": "0101010101",
  "email": "user@example.com",
  "password": "securePassword1",
  "confirmPassword": "securePassword1",
  "student": {
    "pricingRuleCode": "TEST",
    "accountPaymentType": "CASH",
    "accountType": null,
    "accessFrom": "2025/01/01",
    "accessUntil": "2045/01/01",
    "class": "Groupe",
    "section": "Section",
    "subSection": "SubSection",
    "studentNumber": "XXXXXXX",
    "tariffCode": "XXX"
  }
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| role | string | "customer" | The role value. |
| brandId | integer | 22 | Identifier of the brand. |
| badgeNumber | integer | 10390970 | The badge number value. |
| firstName | string | "First" | The first name value. |
| lastName | string | "Last" | The last name value. |
| phone | string | "0101010101" | Phone number. |
| email | string | "user@example.com" | Email address. |
| password | string | "securePassword1" | The password value. |
| confirmPassword | string | "securePassword1" | The confirm password value. |
| student | object | {…} | Object containing student fields. |
| student.pricingRuleCode | string | "TEST" | The pricing rule code value. |
| student.accountPaymentType | string | "CASH" | The account payment type value. |
| student.accountType | null | null | The account type value. |
| student.accessFrom | string | "2025/01/01" | The access from value. |
| student.accessUntil | string | "2045/01/01" | The access until value. |
| student.class | string | "Groupe" | The class value. |
| student.section | string | "Section" | The section value. |
| student.subSection | string | "SubSection" | The sub section value. |
| student.studentNumber | string | "XXXXXXX" | The student number value. |
| student.tariffCode | string | "XXX" | The tariff code value. |

#### Response

```json
{
  "status": 201,
  "code": "customer_create_succeed",
  "message": "L'utilisateur a été créé avec succès.",
  "data": {
    "customerId": 5337578,
    "brandId": 22,
    "badgeNumber": "10390970",
    "firstName": "First",
    "lastName": "Last",
    "email": "user@example.com",
    "student": {
      "studentId": 3703477,
      "pricingRuleCode": "TEST",
      "accountPaymentType": "CASH",
      "class": "Groupe",
      "section": "Section",
      "studentNumber": "XXXXXXX",
      "customerId": 5337578
    },
    "badges": [
      {
        "badgeId": 5778270,
        "value": "10390970",
        "type": "classic",
        "isDefault": true
      }
    ]
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 201 | HTTP status code returned by the API. |
| code | string | "customer\_create\_succeed" | Machine-readable application code for the result. |
| message | string | "L'utilisateur a été créé avec succès." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.customerId | integer | 5337578 | Identifier of the customer. |
| data.brandId | integer | 22 | Identifier of the brand. |
| data.badgeNumber | string | "10390970" | The badge number value. |
| data.firstName | string | "First" | The first name value. |
| data.lastName | string | "Last" | The last name value. |
| data.email | string | "user@example.com" | Email address. |
| data.student | object | {…} | Object containing student fields. |
| data.student.studentId | integer | 3703477 | Identifier of the associated student. |
| data.student.pricingRuleCode | string | "TEST" | The pricing rule code value. |
| data.student.accountPaymentType | string | "CASH" | The account payment type value. |
| data.student.class | string | "Groupe" | The class value. |
| data.student.section | string | "Section" | The section value. |
| data.student.studentNumber | string | "XXXXXXX" | The student number value. |
| data.student.customerId | integer | 5337578 | Identifier of the customer. |
| data.badges | array | \[…\] | List of badges entries. |
| data.badges\[\] | object | {…} | Object containing badges fields. |
| data.badges\[\].badgeId | integer | 5778270 | Identifier of the associated badge. |
| data.badges\[\].value | string | "10390970" | The value value. |
| data.badges\[\].type | string | "classic" | The type value. |
| data.badges\[\].isDefault | boolean | true | Whether default is enabled or applies. |

* * *

### 2\. Hierarchy-Based Creation

With the Hierarchy feature enabled (typically for corporate canteens), you cannot use free text for organization fields. Instead, you must link the customer to a specific **Guest Group** (Company, Service, or Sub-service).

##### Before You Start

You need to retrieve the valid `guestGroupId` and `pricingRuleId` for your customer.  
Please refer to the [Hierarchy & Groups](https://developers.innovorder.io/docs/customers/customers-hierarchy-groups.md) to learn how to traverse the organization tree.

#### Create Request

Use the IDs retrieved from the hierarchy traversal in the payload.

### `POST /brands/{brandId}/customers` - Create (Hierarchy)

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| brandId | integer | Yes | The unique identifier of the brand that owns the customer. |

#### Request Body

```json
{
  "firstName": "Alice",
  "lastName": "Doe",
  "email": "alice.doe@example.com",
  "password": "SecurePassword123!",
  "confirmPassword": "SecurePassword123!",
  "badgeNumber": "88293778",
  "brandId": 496,
  "role": "customer",
  "phone": "0101010101",
  "badges": [
    {
      "value": "88293778",
      "type": "",
      "isDefault": true
    },
    {
      "value": "81595056",
      "type": "secondary",
      "isDefault": false
    }
  ],
  "student": {
    "accountPaymentType": "PREPAYMENT",
    "accountType": null,
    "guestGroupId": 729,
    "pricingRuleId": 11469,
    "studentNumber": "matricule111111",
    "accessFrom": "2025-11-20",
    "accessUntil": "2033-10-23"
  }
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| firstName | string | "Alice" | The first name value. |
| lastName | string | "Doe" | The last name value. |
| email | string | "alice.doe@example.com" | Email address. |
| password | string | "SecurePassword123!" | The password value. |
| confirmPassword | string | "SecurePassword123!" | The confirm password value. |
| badgeNumber | string | "88293778" | The badge number value. |
| brandId | integer | 496 | Identifier of the brand. |
| role | string | "customer" | The role value. |
| phone | string | "0101010101" | Phone number. |
| badges | array | \[…\] | List of badges entries. |
| badges\[\] | object | {…} | Object containing badges fields. |
| badges\[\].value | string | "88293778" | The value value. |
| badges\[\].type | string | "" | The type value. |
| badges\[\].isDefault | boolean | true | Whether default is enabled or applies. |
| student | object | {…} | Object containing student fields. |
| student.accountPaymentType | string | "PREPAYMENT" | The account payment type value. |
| student.accountType | null | null | The account type value. |
| student.guestGroupId | integer | 729 | Identifier of the associated guest group. |
| student.pricingRuleId | integer | 11469 | Identifier of the associated pricing rule. |
| student.studentNumber | string | "matricule111111" | The student number value. |
| student.accessFrom | string | "2025-11-20" | The access from value. |
| student.accessUntil | string | "2033-10-23" | The access until value. |

#### Response

```json
{
  "status": 201,
  "code": "customer_create_succeed",
  "message": "Customer has been successfully created.",
  "data": {
    "customerId": 5337578,
    "brandId": 496,
    "badgeNumber": "88293778",
    "firstName": "Alice",
    "lastName": "Doe",
    "email": "alice.doe@example.com",
    "student": {
      "studentId": 3703477,
      "customerId": 5337578,
      "guestGroupId": 729,
      "pricingRuleId": 11469,
      "accountPaymentType": "PREPAYMENT",
      "studentNumber": "matricule111111"
    },
    "badges": [
      {
        "badgeId": 5778270,
        "value": "88293778",
        "type": "classic",
        "isDefault": true
      }
    ]
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 201 | HTTP status code returned by the API. |
| code | string | "customer\_create\_succeed" | Machine-readable application code for the result. |
| message | string | "Customer has been successfully created." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.customerId | integer | 5337578 | Identifier of the customer. |
| data.brandId | integer | 496 | Identifier of the brand. |
| data.badgeNumber | string | "88293778" | The badge number value. |
| data.firstName | string | "Alice" | The first name value. |
| data.lastName | string | "Doe" | The last name value. |
| data.email | string | "alice.doe@example.com" | Email address. |
| data.student | object | {…} | Object containing student fields. |
| data.student.studentId | integer | 3703477 | Identifier of the associated student. |
| data.student.customerId | integer | 5337578 | Identifier of the customer. |
| data.student.guestGroupId | integer | 729 | Identifier of the associated guest group. |
| data.student.pricingRuleId | integer | 11469 | Identifier of the associated pricing rule. |
| data.student.accountPaymentType | string | "PREPAYMENT" | The account payment type value. |
| data.student.studentNumber | string | "matricule111111" | The student number value. |
| data.badges | array | \[…\] | List of badges entries. |
| data.badges\[\] | object | {…} | Object containing badges fields. |
| data.badges\[\].badgeId | integer | 5778270 | Identifier of the associated badge. |
| data.badges\[\].value | string | "88293778" | The value value. |
| data.badges\[\].type | string | "classic" | The type value. |
| data.badges\[\].isDefault | boolean | true | Whether default is enabled or applies. |

### Payload Reference

Detailed description of all fields accepted in the `POST /customers` payload.

| Field | Type | Description |
| --- | --- | --- |
| role | String | Mandatory. Value must be `"customer"`. |
| brandId | Integer | Mandatory. The unique ID of the brand. |
| firstName | String | Customer's first name. |
| lastName | String | Customer's last name. |
| email | String | Unique email address. |
| phone | String | Phone number (optional but recommended). |
| password | String | Password for the account. |
| confirmPassword | String | Must match the `password` field. |
| badgeNumber | String | A single primary badge number. |
| badges | Array&lt;Badge&gt; | Advanced badge assignment. Contains objects with:<br>-   `value`: The badge number string.<br>-   `type`: e.g. "classic", "secondary".<br>-   `isDefault`: Boolean. |
| **student (Object)** | | |
| student.studentNumber | String | Unique identifier (Matricule). |
| student.accountPaymentType | String | `"CASH"`, `"PREPAYMENT"` or `"POSTPAYMENT"`. |
| student.accessFrom | String | Start date of validity (YYYY-MM-DD or ISO). |
| student.accessUntil | String | End date of validity (YYYY-MM-DD or ISO). |
| **Standard Mode Only** | | |
| student.class | String | Free text field for Class/Group. |
| student.section | String | Free text field for Section. |
| student.subSection | String | Free text field for Sub-section. |
| student.pricingRuleCode | String | Free text code for pricing rule. |
| **Hierarchy Mode Only** | | |
| student.guestGroupId | Integer | ID of the selected group (leaf node) from `GET /guest_groups`. |
| student.pricingRuleId | Integer | ID of the pricing rule from `GET /pricing_rules`. |

### Update Customer

Update an existing customer profile. You can modify personal information, password, badge number, student details, and e-wallet settings.

### `PUT /customers/{customerId}` - Update Customer

Update a customer by their ID. Only the fields provided in the body will be updated. Supports partial updates.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| customerId | integer | Yes | The unique identifier of the customer. |
| autoImportGuestHierarchy | boolean | No | If true, automatically imports guest hierarchy when updating student fields. |

#### Request Body

```json
{
  "firstName": "Alice",
  "lastName": "Doe-Updated",
  "phone": "0601020304",
  "email": "alice.updated@example.com",
  "badgeNumber": "10390970",
  "student": {
    "accessUntil": "2035-12-31"
  }
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| firstName | string | "Alice" | The first name value. |
| lastName | string | "Doe-Updated" | The last name value. |
| phone | string | "0601020304" | Phone number. |
| email | string | "alice.updated@example.com" | Email address. |
| badgeNumber | string | "10390970" | The badge number value. |
| student | object | {…} | Object containing student fields. |
| student.accessUntil | string | "2035-12-31" | The access until value. |

#### Response

```json
{
  "status": 200,
  "code": "customer_update_succeed",
  "message": "L'utilisateur a bien ete mis a jour.",
  "data": {
    "customerId": 5337578,
    "firstName": "Alice",
    "lastName": "Doe-Updated",
    "phone": "0601020304",
    "email": "alice.updated@example.com",
    "badgeNumber": "10390970",
    "brandId": 22,
    "student": {
      "studentId": 3703477,
      "pricingRuleCode": "TEST",
      "accountPaymentType": "CASH",
      "accessUntil": "2035-12-31T00:00:00.000Z",
      "customerId": 5337578
    }
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "customer\_update\_succeed" | Machine-readable application code for the result. |
| message | string | "L'utilisateur a bien ete mis a jour." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.customerId | integer | 5337578 | Identifier of the customer. |
| data.firstName | string | "Alice" | The first name value. |
| data.lastName | string | "Doe-Updated" | The last name value. |
| data.phone | string | "0601020304" | Phone number. |
| data.email | string | "alice.updated@example.com" | Email address. |
| data.badgeNumber | string | "10390970" | The badge number value. |
| data.brandId | integer | 22 | Identifier of the brand. |
| data.student | object | {…} | Object containing student fields. |
| data.student.studentId | integer | 3703477 | Identifier of the associated student. |
| data.student.pricingRuleCode | string | "TEST" | The pricing rule code value. |
| data.student.accountPaymentType | string | "CASH" | The account payment type value. |
| data.student.accessUntil | string | "2035-12-31T00:00:00.000Z" | The access until value. |
| data.student.customerId | integer | 5337578 | Identifier of the customer. |

#### E-Wallet Overdraft & Daily Limit

To modify overdraft or daily payment limit fields (`overrideEwalletOverdraft`, `ewalletOverdraftAmount`, `overrideDailyPaymentLimit`, `paymentDailyLimit`), the authenticated user must have the `CAN_MANAGE_CUSTOMER_EWALLET_OVERDRAFT` access flag.

### Find Customers

Search for customers using various criteria or retrieve a specific user's information using their badge number or ID.

### `GET /customers` - Search Customers

Retrieve a list of customers based on filters. Useful for finding a user by email, name, or student number. Note that hierarchy fields are exposed at the root level. Restaurant-role tokens are lookup-only: an identity search (search, firstName, lastName, email, badgeNumber, studentNumber or emails) of at least 3 characters is required (bulk filters alone are refused with 400 customer\_search\_term\_required), and the visible window is capped at 25 rows (limit is clamped, count never exceeds 25). Brand, brandGroup, admin and POS tokens are unaffected.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| brandId | string | Yes | The brand Id of the current business. |
| restaurantIds | string | No | Filter by specific restaurant IDs (comma separated). |
| search | string | No | Partial match on name, email, phone, or badge number. |
| studentNumber | string | No | Exact match on the unique student reference. |
| include | string | No | Include additional data: ewalletBalance, grant, entranceFee, pricingRule. |
| unitCurrency | boolean | No | If true, returns monetary values in currency units (e.g. 12.50) instead of cents. |
| mustHaveBadgeNumber | boolean | No | If true, only returns users with an assigned badge number. |

#### Response

```json
{
  "status": 200,
  "code": "customers_succeed",
  "message": "Vous pouvez accéder à ces clients",
  "data": {
    "count": 2695,
    "customers": [
      {
        "customerId": 2792744,
        "firstName": "G CAVADINI",
        "lastName": "QUALITE INVITE",
        "email": "399@sodexo.com",
        "badgeNumber": "399",
        "studentNumber": "399",
        "class": "NEWTON",
        "section": "INVITE",
        "subSection": null,
        "pricingRuleCode": "INVITE",
        "accountPaymentType": "POSTPAYMENT",
        "guestGroupId": 3043,
        "balance": 0
      },
      {
        "customerId": 2792746,
        "firstName": "FONDATION",
        "lastName": "CARTE INVITE",
        "email": "30126@sodexo.com",
        "class": "BEL",
        "section": "INVITE",
        "subSection": null,
        "guestGroupId": 3219
      }
    ]
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "customers\_succeed" | Machine-readable application code for the result. |
| message | string | "Vous pouvez accéder à ces clients" | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.count | integer | 2695 | Total number of matching records. |
| data.customers | array | \[…\] | List of customers entries. |
| data.customers\[\] | object | {…} | Object containing customers fields. |
| data.customers\[\].customerId | integer | 2792744 | Identifier of the customer. |
| data.customers\[\].firstName | string | "G CAVADINI" | The first name value. |
| data.customers\[\].lastName | string | "QUALITE INVITE" | The last name value. |
| data.customers\[\].email | string | "399@sodexo.com" | Email address. |
| data.customers\[\].badgeNumber | string | "399" | The badge number value. |
| data.customers\[\].studentNumber | string | "399" | The student number value. |
| data.customers\[\].class | string | "NEWTON" | The class value. |
| data.customers\[\].section | string | "INVITE" | The section value. |
| data.customers\[\].subSection | null | null | The sub section value. |
| data.customers\[\].pricingRuleCode | string | "INVITE" | The pricing rule code value. |
| data.customers\[\].accountPaymentType | string | "POSTPAYMENT" | The account payment type value. |
| data.customers\[\].guestGroupId | integer | 3043 | Identifier of the associated guest group. |
| data.customers\[\].balance | integer | 0 | The balance value. |

### `GET /customers/{customerId}` - Get Customer Details

Retrieve detailed information for a single customer by their ID. Hierarchy information is nested within the \`student\` object.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| customerId | integer | Yes | The unique identifier of the customer. |
| unscoped | boolean | No | Set to true to bypass certain scope checks if necessary. |

#### Response

```json
{
  "status": 200,
  "code": "customer_succeed",
  "message": "Vous pouvez accéder à ce client",
  "data": {
    "customerId": 2792749,
    "firstName": "LONS",
    "lastName": "CARTE INVITE",
    "phone": null,
    "email": "395@sodexo.com",
    "badgeNumber": "395",
    "brandId": 2158,
    "ewalletOverdraftAmount": 1000000,
    "created_at": "2023-06-09T16:40:42.000Z",
    "student": {
      "studentId": 1749852,
      "studentNumber": "395",
      "class": "BEL",
      "section": "110900",
      "subSection": null,
      "pricingRuleCode": "INVITE",
      "customerId": 2792749,
      "accountPaymentType": "POSTPAYMENT",
      "accountType": "POSTPAYMENT",
      "guestGroupId": 3207,
      "pricingRuleId": 5771
    },
    "badges": [
      {
        "badgeId": 3887136,
        "value": "395",
        "type": "classic",
        "isDefault": true
      }
    ],
    "ewalletAutoReload": false
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "customer\_succeed" | Machine-readable application code for the result. |
| message | string | "Vous pouvez accéder à ce client" | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.customerId | integer | 2792749 | Identifier of the customer. |
| data.firstName | string | "LONS" | The first name value. |
| data.lastName | string | "CARTE INVITE" | The last name value. |
| data.phone | null | null | Phone number. |
| data.email | string | "395@sodexo.com" | Email address. |
| data.badgeNumber | string | "395" | The badge number value. |
| data.brandId | integer | 2158 | Identifier of the brand. |
| data.ewalletOverdraftAmount | integer | 1000000 | The ewallet overdraft amount value. |
| data.created\_at | string | "2023-06-09T16:40:42.000Z" | Timestamp when this resource was created. |
| data.student | object | {…} | Object containing student fields. |
| data.student.studentId | integer | 1749852 | Identifier of the associated student. |
| data.student.studentNumber | string | "395" | The student number value. |
| data.student.class | string | "BEL" | The class value. |
| data.student.section | string | "110900" | The section value. |
| data.student.subSection | null | null | The sub section value. |
| data.student.pricingRuleCode | string | "INVITE" | The pricing rule code value. |
| data.student.customerId | integer | 2792749 | Identifier of the customer. |
| data.student.accountPaymentType | string | "POSTPAYMENT" | The account payment type value. |
| data.student.accountType | string | "POSTPAYMENT" | The account type value. |
| data.student.guestGroupId | integer | 3207 | Identifier of the associated guest group. |
| data.student.pricingRuleId | integer | 5771 | Identifier of the associated pricing rule. |
| data.badges | array | \[…\] | List of badges entries. |
| data.badges\[\] | object | {…} | Object containing badges fields. |
| data.badges\[\].badgeId | integer | 3887136 | Identifier of the associated badge. |
| data.badges\[\].value | string | "395" | The value value. |
| data.badges\[\].type | string | "classic" | The type value. |
| data.badges\[\].isDefault | boolean | true | Whether default is enabled or applies. |
| data.ewalletAutoReload | boolean | false | The ewallet auto reload value. |

### Customer Channel and Badge Status

Use these read-only endpoints to decide whether a customer can use a sales channel and to retrieve every disabled or active badge associated with the customer.

### `GET /customers/{customerId}/channel/{channelId}` - Check Customer Channel Access

Checks the customer access rule for one sales channel. This does not create an order or alter the customer.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| customerId | integer | Yes | Customer whose channel access is checked. |
| channelId | integer | Yes | Sales channel identifier to check. |

#### Response

```json
{
  "status": 200,
  "code": "customer_succeed",
  "message": "You can access to this customer.",
  "data": {
    "customerId": 2792749,
    "channelId": 2,
    "hasAccessToChannel": true
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "customer\_succeed" | Machine-readable application code for the result. |
| message | string | "You can access to this customer." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.customerId | integer | 2792749 | Identifier of the customer. |
| data.channelId | integer | 2 | Identifier of the associated channel. |
| data.hasAccessToChannel | boolean | true | Whether access to channel is enabled or applies. |

### `GET /customers/{customerId}/disabled_badges` - List Customer Disabled Badges

Returns disabled badges plus the currently assigned active badge, if the customer has one. The active badge is returned with disabledAt set to null.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| customerId | integer | Yes | Customer whose badge history is retrieved. |

#### Response

```json
{
  "status": 200,
  "code": "customer_badges_succeed",
  "message": "The customer's badges have been found.",
  "data": [
    {
      "badgeNumber": "OLD-395",
      "brandId": 2158,
      "customerId": 2792749,
      "isBadgeBlocked": true,
      "disabledAt": "2026-07-01T09:00:00.000Z"
    },
    {
      "badgeNumber": "395",
      "brandId": 2158,
      "customerId": 2792749,
      "isBadgeBlocked": false,
      "disabledAt": null
    }
  ]
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "customer\_badges\_succeed" | Machine-readable application code for the result. |
| message | string | "The customer's badges have been found." | Human-readable result message. Do not use this value for program logic. |
| data | array | \[…\] | Endpoint-specific response payload. |
| data\[\] | object | {…} | Endpoint-specific response payload. |
| data\[\].badgeNumber | string | "OLD-395" | The badge number value. |
| data\[\].brandId | integer | 2158 | Identifier of the brand. |
| data\[\].customerId | integer | 2792749 | Identifier of the customer. |
| data\[\].isBadgeBlocked | boolean | true | Whether badge blocked is enabled or applies. |
| data\[\].disabledAt | string | "2026-07-01T09:00:00.000Z" | Date or timestamp for disabled. |

### Customer Login

Authenticate a customer for a specific brand to obtain an access token. This token is required for all customer-scoped operations, such as checking balances or placing orders.

#### Important Scope Distinction

Unlike administrative users, a customer login **must** include the `brandId` in the request body. This ensures the authentication is performed against the correct customer database for that specific brand.

### `POST /oauth/login` - Customer Login

Authenticate a customer using their credentials and the brand ID. The access token is valid for 24 hours (14 days when rememberMe is true); when it expires the API responds with a 401 error (token\_expired) and the customer must authenticate again.

#### Request Body

```json
{
  "username": "alice.doe@example.com",
  "password": "SecurePassword123!",
  "grant_type": "password",
  "brandId": 100,
  "rememberMe": true
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| username | string | "alice.doe@example.com" | The username value. |
| password | string | "SecurePassword123!" | The password value. |
| grant\_type | string | "password" | The grant type value. |
| brandId | integer | 100 | Identifier of the brand. |
| rememberMe | boolean | true | The remember me value. |

#### Response

```json
{
  "access_token": "eyJhbGciOiJIUzI1...",
  "token_type": "Bearer",
  "status": 201,
  "code": "token_created",
  "message": "You have successfully logged in.",
  "data": {
    "user": {
      "userId": 5337578,
      "email": "alice.doe@example.com",
      "role": "customer",
      "brandId": 100,
      "firstName": "Alice",
      "lastName": "Doe"
    }
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| access\_token | string | "eyJhbGciOiJIUzI1..." | The access token value. |
| token\_type | string | "Bearer" | The token type value. |
| status | integer | 201 | HTTP status code returned by the API. |
| code | string | "token\_created" | Machine-readable application code for the result. |
| message | string | "You have successfully logged in." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.user | object | {…} | Object containing user fields. |
| data.user.userId | integer | 5337578 | Identifier of the user. |
| data.user.email | string | "alice.doe@example.com" | Email address. |
| data.user.role | string | "customer" | The role value. |
| data.user.brandId | integer | 100 | Identifier of the brand. |
| data.user.firstName | string | "Alice" | The first name value. |
| data.user.lastName | string | "Doe" | The last name value. |
