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

# Identifiers and the envelope

> Prefixed ids, whole dollars, RFC 3339 timestamps, null against absent, the response envelope, the request id, the cursor and the status rule.

<Note>
  The Marketplace API is in a private beta. It answers only for accounts Investorlift has enabled. [Access](/marketplace/access) says how to ask for one. A route shape on these pages can change before the beta ends. The changelog records every change.
</Note>

Every resource of the Marketplace API follows the same conventions, and this page states them once. The
[God Mode envelope](/guides/concepts/envelope) has the same three top-level keys. The ids, the money rule and the status
rule are the marketplace's own.

## Identifiers

A public id is the row id with a type prefix and an underscore, for example `mdl_a1b2c3d4e5f6`. The prefix says what the
id names. Store the id whole, prefix included, and send it back as the API served it. The id encodes its creation
instant, so ids sort in creation order. Every deal, draft, offer, inquiry, lead, buy box and webhook endpoint also
carries `created_at`.

| Prefix | What it names                                                                                    |
| ------ | ------------------------------------------------------------------------------------------------ |
| `drf_` | A draft deal, before it takes a property slot.                                                   |
| `mdl_` | A live deal.                                                                                     |
| `ofr_` | One round of an offer chain.                                                                     |
| `inq_` | An inquiry or an address request.                                                                |
| `byr_` | A buyer.                                                                                         |
| `slr_` | A seller organization.                                                                           |
| `mbr_` | A member of a seller organization. `disposition_manager_id` and `actor.id` on an event carry it. |
| `stk_` | A strike.                                                                                        |
| `rev_` | A review.                                                                                        |
| `whk_` | A webhook endpoint.                                                                              |
| `evt_` | An event.                                                                                        |
| `bbx_` | A buy box.                                                                                       |
| `hld_` | A held write. A `202` answer carries it as `hold_id`.                                            |

A lead has no id of its own. The route addresses it by its deal and its buyer: `/sell/deals/{deal_id}/leads/{buyer_id}`.

Some ids carry no prefix. A media item and a document carry a plain `id`, which has a meaning only inside their draft
or their deal. `client_id` names an OAuth client, and `agreement.id` names an agreement. Store them as strings.

An id of the wrong form answers `400 invalid_id`. An id of the right form that names nothing your account owns answers
`404 not_found`. The API does not tell a foreign id from an unknown one. The God Mode API uses other prefixes
(`deal_`, `prop_`, `inv_`), and an id never crosses the two APIs.

## Values

* **Money** is a whole number of US dollars, never a float and never cents: `asking_price`, `offer_amount`,
  `emd_amount`.
* **Timestamps** are RFC 3339 strings in UTC, for example `2026-10-02T14:03:00Z`. A deal, a draft, an offer, an
  inquiry, a lead, a buy box and a webhook endpoint carry `created_at` and `updated_at`.
* **Null means not entered.** A null `entity_name` says the buyer linked no legal entity.
* **A field you cannot see is absent, never null.** A token without `contacts:read` gets no `email` key on a buyer
  block. Test for the key, and your code tells "not allowed" from "not entered". One exception: a recommended lead
  carries `buyer: null` until the person acts, because the key is part of every lead row. Read `origin` to tell the two
  apart.
* **A hidden address** is `address: { status: "hidden", city, state, zip }` with `street` absent. Its coordinates sit on
  a coarse grid, and `is_approximate` is true. Once the seller shares the street, `status` reads `approved` and `street`
  appears. On the deal read, `status` also reads `requested` after your address request, and `declined` after the seller
  declines it. A search row carries `hidden` or `approved` only.

## The envelope

One resource answers `{ data, meta }`. A list answers `{ data, page, meta }`. This is a page of one row:

```json theme={null}
{
  "data": [
    {
      "id": "mdl_a1b2c3d4e5f6",
      "status": "available",
      "asking_price": 245000,
      "created_at": "2026-10-02T14:03:00Z",
      "updated_at": "2026-10-02T14:03:00Z"
    }
  ],
  "page": { "next_cursor": null, "limit": 25, "returned": 1 },
  "meta": {
    "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
    "api_version": "2026-09-18"
  }
}
```

| Key    | What it is                                                                                                                                           |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data` | The resource, or the rows of this page.                                                                                                              |
| `page` | `next_cursor` is the token of the next page, or null at the end. `limit` is the page size the API used. `returned` is how many rows this page holds. |
| `meta` | `request_id` is the id of this call. `api_version` is the contract version of the body, a date.                                                      |

Every response also carries the request id as the `X-Request-Id` header, and every problem body carries it as
`request_id`. Log it, and quote it to support. A webhook endpoint pins `api_version` at create
([Webhooks and events](/marketplace/concepts/webhooks-and-events)).

## Compatibility

`/marketplace/v1` is additive. A new field, a new enum value or a new event type arrives without notice, and your client
ignores what it does not know. A removal or a rename gets 30 days of email notice and a line in the
[changelog](/changelog). `api_version` is the date of the contract. A webhook endpoint pins it at create. A payload
change ships as a new date, and you opt into it with a new endpoint.

## Pagination

Ask for a page. Read `page.next_cursor`. Send it back as `cursor=` with the same query. If it is null, stop. A short
page is not the end of the list. Only a null cursor is.

The cursor is opaque. The API binds it to your account and to the query. A cursor from another account, another query
or another list answers `400 invalid_parameter` with `parameter: "cursor"`. Read the first page again. The events feed
is the one exception, because its cursor is a sequence position and a changed `types=` keeps it valid
([Webhooks and events](/marketplace/concepts/webhooks-and-events#the-events-feed)).

A page carries 25 rows by default. Where a list takes `limit=`, the parameter sets the page size, up to 100 rows. The
two lead lists and the seller's inquiry list serve 100 rows by default. A buyer's own offers list and own inquiries list
take at most 25 rows. The deal search and the two review lists take no `limit=` and serve 25 rows. The two events feeds
serve 50 rows by default.

`updated_since=` is the sync primitive. The lists that take it sort by `updated_at`, newest first, then by id. Keep the
`updated_at` of the newest row you stored. On the next run, send it as
`updated_since=`, and the list carries only the rows that changed after that instant. Page through with the cursor as
usual. Do not compute a page count: the API never counts the whole set for you.

## The status rule

| Status | What it means                                                                                                                             | The codes                                                                                                                                                                                                                                                                                                                                                                       |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `409`  | The current state of the resource refuses the action. Read the resource, then act on its current state.                                   | `deal_closed`, `offer_exists`, `offer_superseded`, `deal_changed`, `draft_under_review`, `address_unavailable`, `offer_required`, `wrong_side`, `lead_locked`, `agreement_required`, `organization_required`, `person_exists`, `strike_exists`, `strike_window_closed`, `resource_exists`, `resource_limit`, `webhook_limit`, `idempotency_conflict`, `idempotency_in_progress` |
| `422`  | The content of the request is unusable. Change the request.                                                                               | `validation_failed`, `deal_incomplete`, `preview_expired`, `preview_mismatch`, `below_floor`, `requirement_unmet`, `invalid_transition`, `proof_of_funds_missing`, `proof_of_funds_expired`, `webhook_url_refused`                                                                                                                                                              |
| `429`  | A cap or a rate limit stopped the request. Wait for `Retry-After`, and read the cap the body names.                                       | `cap_reached`, `daily_cap_reached`, `duplicate_inquiry`, `rate_limited`                                                                                                                                                                                                                                                                                                         |
| `202`  | A held write. Not a problem: `data.status` reads `held`, `data.hold_id` names the hold, and the write waits for a person at Investorlift. |                                                                                                                                                                                                                                                                                                                                                                                 |

The two webhook test posts also answer `202`. Their `data` names the ping event, and nothing is on hold.

[Errors](/marketplace/concepts/errors) lists every code with its recovery sentence.
[Trust and limits](/marketplace/concepts/trust-and-limits#the-hold-queue) explains the hold queue.


## Related topics

- [Errors](/marketplace/concepts/errors.md)
- [Authentication](/marketplace/authentication.md)
- [Webhooks and events](/marketplace/concepts/webhooks-and-events.md)
- [Idempotency](/marketplace/concepts/idempotency.md)
- [The response envelope](/api-reference/objects/envelope.md)
