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

# API Responses

Every response from the KYC API uses a consistent structure.

<Tip>
  Read this page carefully as it contains key details to make your integration successful.
</Tip>

This page describes the two shapes you'll encounter:

* Success envelope for all successful requests
* Error object for anything that goes wrong

## Success responses

All successful responses wrap their payload in a top-level envelope with two fields:

* object - The requested resource or collection
* meta - Metadata about the request itself

## Single resource

A request for a single resource — such as GET /v1/guests/{guestId} — returns data as an object.

```json theme={null}
{
  "data": {
    "object": "guest",
    "id": "9c8b1e4f-33a2-4d71-b6e0-8f2c7d0ae1f3",
    "firstName": "Amara",
    "lastName": "Osei",
    "email": "amara.osei@example.com",
    "createdAt": "2026-01-15T09:00:00.000Z",
    "updatedAt": "2026-06-10T14:22:11.000Z"
  },
  "meta": {
    "requestId": "req_01J4XKBZ8FGHJT3P0VNSCQDR7E",
    "timestamp": "2026-08-08T12:00:00.000Z"
  }
}
```

## Collection

A request for a list of resources — such as GET /v1/rooms — returns data as an array, and meta includes a pagination object.

```json theme={null}
{
  "data": [
    {
      "object": "room",
      "id": "a1b2c3d4-0000-0000-0000-000000000001",
      "number": "101",
      "type": "deluxe_king",
      "status": "available"
    },
    {
      "object": "room",
      "id": "a1b2c3d4-0000-0000-0000-000000000002",
      "number": "102",
      "type": "deluxe_king",
      "status": "occupied"
    }
  ],
  "meta": {
    "requestId": "req_01J4XKBZ8FGHJT3P0VNSCQDR7F",
    "timestamp": "2026-08-08T12:00:00.000Z",
    "pagination": {
      "page": 1,
      "pageSize": 20,
      "totalPages": 5,
      "totalItems": 94
    }
  }
}
```

## The meta object

meta is present on every successful response, whether a single resource or a collection.

* requestId - string (UUID) - A unique identifier for this request, generated by KYC. Use this when contacting support. It is also returned in the Kyc-Request-Id response header.
* timestamp - string (ISO 8601) - UTC timestamp of when the response was generated.
* pagination object - Present on collection responses only. See Pagination for details.

## The object field

Every resource in the data payload includes an "object" field that identifies its type.
This is present on top-level resources and on any nested sub-resources.

```json theme={null}
{ "object": "guest", ... }
{ "object": "room", ... }
{ "object": "phone", ... }
```

This lets you identify what you're working with regardless of context —
useful when processing webhook payloads, polymorphic responses, or nested objects.

See the OpenAPI specification for each object for each field’s purpose, types, validation, etc.

## Nested objects — sub-resources and value objects

Resources can contain nested objects of two kinds. Understanding the difference matters because they behave differently in the API.

Sub-resources are nested objects that have their own identity — they carry an id and an object field. They can typically be read or updated through their own dedicated endpoint (now or in a future API version), even if they are also returned inline as part of a parent resource. Examples: phone, email, reservation, message.

Value objects are pure attribute bundles with no identity of their own. They have no id and no object field, and they have no standalone endpoint — they only exist as part of their parent. To update a value object you replace it wholesale as part of a parent update. Examples: address, a money amount, a coordinate pair.

A guest with both kinds of nested object looks like this:

```json theme={null}
{
  "data": {
    "object": "guest",
    "id": "9c8b1e4f-33a2-4d71-b6e0-8f2c7d0ae1f3",
    "firstName": "Amara",
    "lastName": "Osei",
    "address": {
      "line1": "100 Main Street",
      "city": "Austin",
      "state": "TX",
      "postalCode": "78701",
      "country": "US"
    },
    "phones": [
      {
        "object": "phone",
        "id": "3a1f8c22-0001-0000-0000-000000000001",
        "type": "mobile",
        "number": "+15125550100",
        "primary": true
      },
      {
        "object": "phone",
        "id": "3a1f8c22-0001-0000-0000-000000000002",
        "type": "home",
        "number": "+15125550101",
        "primary": false
      }
    ],
    "emails": [
      {
        "object": "email",
        "id": "7d9e1b44-0002-0000-0000-000000000001",
        "address": "amara.osei@example.com",
        "primary": true
      }
    ],
    "createdAt": "2026-01-15T09:00:00.000Z",
    "updatedAt": "2026-06-10T14:22:11.000Z"
  },
  "meta": {
    "requestId": "req_01J4XKBZ8FGHJT3P0VNSCQDR7E",
    "timestamp": "2026-08-08T12:00:00.000Z"
  }
}
```

You can see:

* address is a value object — no id, no object, replaced as a unit.
* phones and emails are arrays of sub-resources — each entry has its own id and object and can be referenced or updated individually.

Quick reference:

## Sub-resource

|                             | Sub-Resource              | Value Object                |
| --------------------------- | ------------------------- | --------------------------- |
| Has ID                      | Yes                       | No                          |
| Has object field            | Yes                       | No                          |
| Own Endpoint (now or later) | Yes                       | No                          |
| How to update               | own endpoint, or inline   | Replace wholesale on parent |
| Examples                    | phone, email, reservation | address                     |

## Nested arrays

Nested arrays (like phones and emails above) are plain JSON arrays —
they are not wrapped in a data/meta envelope and they ***do not paginate***.
They represent a bounded set of items that belong to the parent resource.

If a collection can grow large or needs independent pagination, it is exposed as its own endpoint
(e.g. GET /v1/guests/{guestId}/messages) rather than embedded in the parent.

## Relations and the ?with= parameter

Sub-resources are not always included in the parent response by default.  For example, a Guest will not include its phone numbers.

Each endpoint documents which nested objects are returned intrinsically and which must be explicitly requested.
To include an optional relation, use the ?with= query parameter:
`GET /v1/guests/{guestId}?with=phones,emails`

When a relation is not requested, its field is omitted entirely from the response —
it will not appear as null or \[]. This is the only case where a field is absent from a response object.
See Absent and empty values below.

## Absent and empty values

The KYC API uses consistent conventions for fields that have no value:

* A scalar field (string, number, date) with no value - we return null — the key is always present
* An array field with no items - we return \[] — the key is always present
* A related resource not requested via ?with= - Field is omitted entirely

We never use an empty string ("") to signal that a value is absent.
If you see null, the field exists but has no value.
If you see a key omitted entirely, it means you did not request that relation.

## Pagination

Collection endpoints use page-based pagination, controlled by two query parameters:

| Parameter | Type    | Default | Description                                                                          |
| --------- | ------- | ------- | ------------------------------------------------------------------------------------ |
| page      | integer | 1       | Page number to return.                                                               |
| pageSize  | integer | 20      | Number of items per page. Maximum varies by endpoint and is documented per endpoint. |

The meta.pagination object in the response tells you where you are and how much data exists:

| Field      | Type    | Description                             |
| ---------- | ------- | --------------------------------------- |
| page       | integer | The current page number.                |
| pageSize   | integer | The number of items in this page.       |
| totalPages | integer | Total number of pages available.        |
| totalItems | integer | Total number of items across all pages. |

To paginate through all results, increment page until page >= totalPages.

## Error responses

When a request cannot be completed, the API returns an error object instead of the success envelope.

Error responses use the application/problem+json content type, following RFC 9457.

```json theme={null}
{
  "type": "https://docs.kychospitality.com/errors/guest-not-found",
  "title": "Guest not found",
  "status": 404,
  "detail": "No guest exists with id 9c8b1e4f-33a2-4d71-b6e0-8f2c7d0ae1f3 for the given property.",
  "instance": "/v1/guests/9c8b1e4f-33a2-4d71-b6e0-8f2c7d0ae1f3",
  "code": "GUEST_NOT_FOUND",
  "errors": [],
  "moreInfo": "",
  "requestId": "req_01J4XKBZ8FGHJT3P0VNSCQDR7G",
  "timestamp": "2026-08-08T12:00:00.000Z"
}
```

### Error fields

All error fields are always present. You will never receive a partially-shaped error object.

| Field     | Type               | Description                                                                                                                                                            |
| --------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type      | string (URI)       | A stable URI that identifies this error type and links to its documentation.                                                                                           |
| title     | string             | A short, human-readable summary of the problem. Does not change between occurrences of the same error type.                                                            |
| status    | integer            | The HTTP status code for this response.                                                                                                                                |
| detail    | string             | A human-readable explanation specific to this occurrence — often includes the offending value or ID.                                                                   |
| instance  | string             | The request path that produced this error.                                                                                                                             |
| code      | string             | A stable, machine-readable error code (e.g. GUEST\_NOT\_FOUND). Use this in your code to handle specific error types — title and detail are for humans and may change. |
| errors    | array              | Field-level validation errors. Present on 400 and 422 responses when specific fields are the cause. Empty array (\[]) otherwise.                                       |
| moreInfo  | string             | Optional inline troubleshooting guidance, beyond what detail covers. Empty string ("") when not applicable.                                                            |
| requestId | string             | The same request identifier returned in meta.requestId on success responses and in the Kyc-Request-Id response header. Include this when contacting support.           |
| timestamp | string (I SO 8601) | UTC timestamp of when the error was generated.                                                                                                                         |

### Error Codes

Error codes are provided for your logic to process, see [Error Codes](/error-codes)

### Field-level errors

When a 400 or 422 response is caused by specific fields in the request, the errors array contains one entry per invalid field:

```json theme={null}
{
  "type": "https://docs.kychospitality.com/errors/validation-failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "The request was well-formed but contained invalid values.",
  "instance": "/v1/guests",
  "code": "VALIDATION_FAILED",
  "errors": [
    {
      "field": "email",
      "code": "INVALID_FORMAT",
      "detail": "Must be a valid email address."
    },
    {
      "field": "phoneNumber",
      "code": "REQUIRED",
      "detail": "This field is required."
    }
  ],
  "moreInfo": "",
  "requestId": "req_01J4XKBZ8FGHJT3P0VNSCQDR7H",
  "timestamp": "2026-08-08T12:00:00.000Z"
}
```

Each entry in errors includes:

| Field  | Type   | Description                                                                                                    |
| ------ | ------ | -------------------------------------------------------------------------------------------------------------- |
| field  | string | The name of the field that failed validation. Uses dot notation for nested fields (e.g. address.postalCode).   |
| code   | string | A stable machine code identifying the validation rule that failed (e.g. REQUIRED, INVALID\_FORMAT, TOO\_LONG). |
| detail | string | A human-readable explanation of what is wrong with this field.                                                 |

## HTTP status codes

The KYC API uses HTTP status codes precisely and consistently.

| Status           | Meaning                                                        |
| ---------------- | -------------------------------------------------------------- |
| 200 - OK         | Request succeeded. Response body contains the result.          |
| 201 - Created    | Resource was created. Response body contains the new resource. |
| 204 - No Content | Request succeeded. No response body (e.g. a DELETE).           |

## Client errors

| Status                       | Meaning                                                                                                                                    |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| 400 Bad Request              | The request is malformed — missing a required header, unknown field in the body, wrong type, bad format. Fix your request before retrying. |
| 401 - Unauthorized           | Authentication failed — your API key is missing, invalid, or revoked.                                                                      |
| 403 - Forbidden              | Authentication succeeded, but your key is not authorized to perform this action or access this resource.                                   |
| 404 - Not Found              | The requested resource does not exist (or has been deleted).                                                                               |
| 405 - Method Not Allowed     | The HTTP verb used is not supported on this endpoint.                                                                                      |
| 409 - Conflict               | The request conflicts with current state — for example, a duplicate resource or a state transition that is not permitted.                  |
| 413 - Content Too Large      | The request body or batch size exceeds the allowed limit.                                                                                  |
| 415 - Unsupported Media Type | The Content-Type header is missing or not application/json on a request with a body.                                                       |
| 422 - Unprocessable Entity   | The request is well-formed, but the values are invalid. Check the errors array for per-field details.                                      |
| 429 - Too Many Requests      | You have exceeded your rate limit. See the Retry-After header for when to try again.                                                       |

## Server errors

| Status                      | Meaning                                                                                                                                   |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| 500 - Internal Server Error | An unexpected error occurred on our side. The error response will often include a requestId — please include it when reporting the issue. |
| 502 - Bad Gateway           | Something is wrong with our API servers - please report this and try again later.                                                         |
| 503 - Service Unavailable   | Something is wrong with our API servers - please report this and try again later.                                                         |
| 504 - Gateway Timeout       | Something is wrong with our API servers - please report this and try again later.                                                         |

## Codes 403 vs. 404

Note that we will often return a 403, unauthorized, instead of a 404, for data that exists but that you do not have access to.
This is done to help find bugs and other issues, such as cross-property data problems,
that would be hard to find if 404s are always returned.  This may change over time.

### Redirects

Redirects are not currently used, but may be used in the future, usually for endpoints that move.
Your code should support them, if possible.

## Response Headers

The API will return specific headers:

| Name               | Purpose                                                                                        |
| ------------------ | ---------------------------------------------------------------------------------------------- |
| Kyc-request-id     | Our internal ID that we log at various steps while processing, essentially our correlation ID. |
| Kyc-correlation-id | Your correlation ID, if you sent it with the request.                                          |
| Traceparent        | Your trace parent ID, if you sent it with the request.                                         |

## E-Tags

E-Tags are not currently used, and thus conditional requests such as last-modified or if-modified-since are not supported, i.e. they will be processed without reading or using those headers.

## Handling errors in your code

A few recommendations for robust error handling:

* Always branch on code, not title or detail. The code field is stable across API versions. Human-readable fields can be reworded without notice.
* Log requestId on every error. It is the fastest way to get support — we can quickly locate the exact request in our systems..
* Tolerate unknown error codes. We may add new code values over time. Your handler should have a catch-all path for codes it does not recognise, rather than treating them as fatal.
* Do not parse detail programmatically. It is a human string intended for developers and support workflows, not for machine logic.

## Compatibility notes

The KYC API evolves additively within a version. When consuming responses:

* Ignore fields you do not recognise. We may add new fields to any response object without a version bump. Strict deserialization that rejects unknown fields will break.
* Handle new enum values gracefully. If a field can grow new values over time, your code should have a safe default for unrecognised values.
* Handle new error codes gracefully. Same principle — new code values may appear as new error conditions are introduced.
* See API Versioning & Compatibility for the full policy on what we consider a breaking change.
