---
internal: true
---

# Admin UI Forms

This guide documents the internal Admin UI form endpoints used to build and apply dynamic order completion forms.

Use this flow when the frontend should render backend-driven form components (customer, address, custom fields, participants) instead of hardcoding field definitions.

---

## Endpoints

- `POST /api/v1/ui/admin/checkout-form`
- `POST /api/v1/ui/admin/checkout-form/apply`
- `POST /api/v1/ui/admin/customer-form`
- `POST /api/v1/ui/admin/address-form`
- `POST /api/v1/ui/admin/order-form`
- `POST /api/v1/ui/admin/booking-form`

All endpoints require:

- `Authorization: Bearer {token}`
- `?o={organization_id}`

---

## Recommended Order Completion Flow

1. Build a checkout form from booking context.
2. Render returned components and collect user input.
3. Apply form data to booking config.
4. Send enriched payload to `POST /api/v1/orders/from-config`.

::::scalar-steps
:::scalar-step{id="admin-ui-form-step-1" title="Build checkout form"}

```http
POST /api/v1/ui/admin/checkout-form?o={organization_id}
Content-Type: application/json
Authorization: Bearer {token}

{
  "booking_config": {
    "order_customer_id": null,
    "bookings": [
      {
        "customer_id": null,
        "service_id": { "{service_id}": 1 },
        "resource_id": "{resource_id}",
        "start_date": "2026-04-15T09:00:00+02:00",
        "end_date": "2026-04-15T10:00:00+02:00"
      },
      {
        "customer_id": "{optional_existing_customer_id}",
        "service_id": { "{service_id}": 1 },
        "resource_id": "{resource_id}",
        "start_date": "2026-04-15T10:00:00+02:00",
        "end_date": "2026-04-15T11:00:00+02:00"
      }
    ]
  }
}
```

The response contains form metadata such as `components`, `validations`, `default`, and `submitAction`.

:::
:::scalar-step{id="admin-ui-form-step-2" title="Apply submitted form data"}

```http
POST /api/v1/ui/admin/checkout-form/apply?o={organization_id}
Content-Type: application/json
Authorization: Bearer {token}

{
  "form_data": {
    "customer": {
      "email": "newcustomer@example.com",
      "given_name": "Test",
      "family_name": "Customer"
    },
    "bookings_data": {
      "b_205": {
        "service_custom_fields": {
          "{booking_field_uuid}": "Wheelchair access"
        },
        "participants": [
          {
            "email": "participant@example.com",
            "given_name": "Alex",
            "family_name": "Example"
          }
        ]
      }
    }
  },
  "booking_config": {
    "bookings": [
      {
        "_booking_id": 205,
        "resource_id": "{resource_id}",
        "service_id": { "{service_id}": 1 },
        "start_date": "2026-04-15T09:00:00+02:00",
        "end_date": "2026-04-15T10:00:00+02:00"
      }
    ]
  }
}
```

The response includes an enriched config with resolved customer IDs and mapped custom fields.

:::
:::scalar-step{id="admin-ui-form-step-3" title="Complete order"}
Use the enriched payload from step 2 as request body for:

```http
POST /api/v1/orders/from-config?o={organization_id}
```

See the public admin guide for full order payload options and error handling.
:::
::::

---

## Standalone Form Endpoints

Use these endpoints when editing existing entities outside the full checkout-form flow.

### Customer Form

```http
POST /api/v1/ui/admin/customer-form?o={organization_id}

{
  "customer_id": "{customer_id}",
  "address_id": "{address_id}",
  "include_address_form": true
}
```

- `customer_id` is optional.
- `address_id` is optional and can be used to prefill address values directly.
- `include_address_form` is optional (`true` by default). Set to `false` to return customer fields without embedding address fields.
- Returns customer fields and (by default) address fields with defaults if customer/address exists.

Customer form payload is split into three write namespaces:

- `customer.*` for customer resource attributes such as `given_name`, `family_name`, `email`, `mobile`, `phone`, `company`, `sex`, and `birth_date`
- `address.*` for address resource attributes such as `street_address`, `zip_code`, `city`, and `country_code`
- `custom_entry_map.{field_uuid}.value` for customer custom fields in the same shape accepted by `PATCH /api/v1/customers/{customer_id}`

Persistence rules:

1. Send `customer.*` as customer attributes to `POST` or `PATCH /api/v1/customers`.
2. Forward `custom_entry_map.*` directly as the customer resource's `custom_entry_map` attribute.
3. Persist `address.*` through the separate `addresses` resource, linked to the customer via `relationships.addressable`.

Typical update flow for an existing customer:

1. `PATCH /api/v1/customers/{customer_id}` with attributes built from `customer.*` plus `custom_entry_map`
2. `PATCH /api/v1/addresses/{address_id}` if the customer already has an address
3. Or `POST /api/v1/addresses` with `relationships.addressable = customers/{customer_id}` if the customer has no address yet

### Address Form

```http
POST /api/v1/ui/admin/address-form?o={organization_id}

{
  "customer_id": "{customer_id}",
  "address_id": "{address_id}"
}
```

- `customer_id` is optional.
- `address_id` is optional and takes precedence for prefilling over `customer_id`.
- Returns address-only components prefilled from the selected address (or from the customer's latest address if only `customer_id` is provided).
- Address field keys use `address.*`.
- Persist the submitted values through the `addresses` resource, linked via `relationships.addressable` when creating a new customer address.

### Order Form

```http
POST /api/v1/ui/admin/order-form?o={organization_id}

{
  "order_id": "{order_id}"
}
```

- `order_id` is required.
- Returns order-level custom field components prefilled from the order.
- Custom field inputs use `custom_entry_map.{field_uuid}.value`, so they can be forwarded directly into the order resource's `custom_entry_map` attribute on update.

### Booking Form

```http
POST /api/v1/ui/admin/booking-form?o={organization_id}

{
  "booking_id": "{booking_id}"
}
```

- `booking_id` is required.
- Returns booking-level custom field components prefilled from the booking.
- Custom field inputs use `custom_entry_map.{field_uuid}.value`, so they can be forwarded directly into the booking resource's `custom_entry_map` attribute on update.
- Use this during order completion when the admin changes booking-specific metadata before creating or updating an order.

---

## Error Handling Notes

- `422` for validation issues (missing required fields, malformed payload).
- `404` when `customer_id`, `order_id`, or `booking_id` does not belong to the active organization.
- `401` or `403` when auth or permissions are missing.
