> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anny.co/llms.txt
> Use this file to discover all available pages before exploring further.

# 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`.

<Steps>
  <Step title="Build checkout form">
    ```http theme={null}
    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`.

    The `booking_config` supports mixed product orders: besides `bookings` it accepts standalone `add_ons` (`[{ "add_on_id": 1, "quantity": 2 }]`) and `booking_passes` purchased from bundles (`[{ "booking_bundle_id": 12, "quantity": 1 }]`). At least one of the three product arrays must be present — `bookings` may be omitted for bundle/add-on-only orders:

    ```json theme={null}
    {
      "booking_config": {
        "order_customer_id": "1701",
        "booking_passes": [
          { "booking_bundle_id": 12, "quantity": 1 }
        ]
      }
    }
    ```
  </Step>

  <Step title="Apply submitted form data">
    ```http theme={null}
    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. Additional config keys such as `add_ons` and `booking_passes` are passed through unchanged, so the result can be sent directly to `POST /api/v1/orders/from-config` for mixed product orders too.
  </Step>

  <Step title="Complete order">
    Use the enriched payload from step 2 as request body for:

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

    See the public admin guide for full order payload options and error handling.
  </Step>
</Steps>

***

## Standalone Form Endpoints

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

### Customer Form

```http theme={null}
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 theme={null}
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 theme={null}
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 theme={null}
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`.
* Persist submitted values with `POST /api/v1/ui/admin/booking-form/apply` (`booking_id` + `form_data`). The apply action writes filled entries, deletes cleared (`""` / `null`) entries, and returns the booking plus a rebuilt `meta.form_default`.
* You can also forward `custom_entry_map` directly into the booking resource on `PATCH /api/v1/bookings/{booking_id}`.
* Use this during [Admin Booking Edit](/developers/guides/admin/booking-edit) when changing booking-specific metadata, or during order completion 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.
