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

# Conventions

> The request and response rules every Crevio endpoint follows — unwrapped params, association ids, creation order, money in cents, pagination, and more.

**The Crevio API is Stripe-shaped, with a few rules that trip up every new integration on day one. Learn them here and the rest of the API is predictable.**

These conventions hold across all of `https://api.crevio.co/v1`. Read this page before writing more than a quickstart call — most `400`/`404`/`422` errors come from breaking one of the rules below.

## Parameters are unwrapped (top-level)

Send request fields at the **top level** of the JSON body. There is **no** resource wrapper key. A nested wrapper like `{"product": {...}}` is silently dropped, leaving the real fields missing — you'll get a `parameter_missing` error.

```bash theme={null}
# Do — fields at the top level
curl https://api.crevio.co/v1/products \
  -H "Authorization: Bearer $CREVIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Intro to Watercolor", "status": "draft" }'
```

```bash theme={null}
# Don't — wrapper is ignored, "name" never arrives
curl https://api.crevio.co/v1/products \
  -H "Authorization: Bearer $CREVIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "product": { "name": "Intro to Watercolor" } }'
```

## Associations are a bare name + a prefixed id

To link a resource, use the **bare resource name** as the key and pass the **prefixed id string** as the value. Do **not** append `_id`.

```jsonc theme={null}
// Do
{ "product": "prod_abc123" }
{ "customer": "cus_abc123" }
{ "order": "ord_abc123" }
{ "discount": "disc_abc123" }
{ "price_variant": "price_abc123" }
```

```jsonc theme={null}
// Don't — these keys are not recognized
{ "product_id": "prod_abc123" }
{ "customer_id": "cus_abc123" }
```

<Warning>
  **Exceptions exist — match the schema field name exactly.** A few fields keep the `_id` suffix because they take ids directly: `site_id` (e.g. on Tasks), `tag_ids` (an array, e.g. on Customers and Forms), `account_ids`, and `upload_ids`. The rule of thumb is "bare name", but check the [API reference](/developer/api-reference/introduction) for the endpoint you're calling.
</Warning>

## Creation order matters

Some resources have prerequisites. The big one: **a product can't be published without at least one price variant.** Creating a product with `status: "active"` in a single call fails with `422`.

<Steps>
  <Step title="Create the product as a draft">
    `POST /products` with `status: "draft"`.
  </Step>

  <Step title="Create one or more price variants">
    `POST /price_variants` with `product: "prod_..."`.
  </Step>

  <Step title="Publish">
    `PATCH /products/{id}` with `status: "active"`.
  </Step>
</Steps>

## Money is in cents; currency is lowercase

All monetary amounts are **integers in the smallest currency unit** (cents for USD). Currency codes are **lowercase** ISO 4217.

```jsonc theme={null}
{ "amount": 4900, "currency": "usd" }   // $49.00 USD
{ "amount": 49.00, "currency": "USD" }  // wrong — not a float, not uppercase
```

## List responses vs single objects

**List endpoints** return a consistent envelope. The items are in `data`.

```json theme={null}
{
  "object": "list",
  "data": [
    { "id": "prod_abc123", "object": "product", "name": "My Course" },
    { "id": "prod_def456", "object": "product", "name": "Templates Pack" }
  ],
  "has_more": true,
  "url": "/v1/products"
}
```

**Single-resource endpoints** return the object directly (no envelope):

```json theme={null}
{ "id": "prod_abc123", "object": "product", "name": "My Course" }
```

## Pagination

Cursor-based. Pass `limit` (1–100, default 20) and `starting_after` (the `id` of the last item you received). When `has_more` is `false`, you're on the last page.

```bash theme={null}
# First page
curl "https://api.crevio.co/v1/products?limit=10" \
  -H "Authorization: Bearer $CREVIO_API_TOKEN"

# Next page — cursor is the last id from the previous page
curl "https://api.crevio.co/v1/products?limit=10&starting_after=prod_def456" \
  -H "Authorization: Bearer $CREVIO_API_TOKEN"
```

## Slug lookup

Products, blog posts, experiences, and legal pages accept **either an id or a slug** in the `{id_or_slug}` path position.

```bash theme={null}
curl https://api.crevio.co/v1/products/prod_abc123   # by id
curl https://api.crevio.co/v1/products/intro-to-watercolor   # by slug
```

## Expanding related resources

By default, related resources appear as bare id strings. Pass `expand` to inline them. Multiple fields are comma-separated.

```bash theme={null}
curl "https://api.crevio.co/v1/orders/ord_abc123?expand=customer,line_items" \
  -H "Authorization: Bearer $CREVIO_API_TOKEN"
```

```jsonc theme={null}
// Without expand
{ "id": "ord_abc123", "object": "order", "customer": "cus_xyz789" }

// With ?expand=customer
{
  "id": "ord_abc123",
  "object": "order",
  "customer": { "id": "cus_xyz789", "object": "customer", "email": "jane@example.com" }
}
```

## Delete responses

Delete endpoints return a confirmation object, not an empty body:

```json theme={null}
{ "id": "disc_abc123", "object": "discount", "deleted": true }
```

## The object discriminator

Every resource carries an `object` field identifying its type. Use it to branch on heterogeneous responses.

```json theme={null}
{ "id": "cus_abc123", "object": "customer", "email": "jane@example.com" }
```

## ID prefixes

Every id is a prefixed string encoding its type. Use these — never raw database ids.

| Resource         | Prefix   | `object` value     |
| ---------------- | -------- | ------------------ |
| Account          | `acct_`  | `account`          |
| Product          | `prod_`  | `product`          |
| Price variant    | `price_` | `price_variant`    |
| Experience       | `exp_`   | `experience`       |
| Order            | `ord_`   | `order`            |
| Order item       | `ordi_`  | `order_item`       |
| Charge           | `ch_`    | `charge`           |
| Customer         | `cus_`   | `customer`         |
| Checkout         | `co_`    | `checkout`         |
| Checkout link    | `cl_`    | `checkout_link`    |
| Invoice          | `inv_`   | `invoice`          |
| Subscription     | `sub_`   | `subscription`     |
| Discount         | `disc_`  | `discount`         |
| Blog post        | `post_`  | `blog_post`        |
| Review           | `rev_`   | `review`           |
| File             | `file_`  | `file`             |
| Form             | `form_`  | `form`             |
| Tag              | `tag_`   | `tag`              |
| Task             | `task_`  | `task`             |
| Task run         | `trun_`  | `task_run`         |
| Site             | `site_`  | `site`             |
| Webhook endpoint | `wh_`    | `webhook_endpoint` |
| Webhook event    | `whev_`  | `webhook_event`    |

<Note>
  Courses and other content live under `/experiences`, not `/products`. A product bundles experiences through its price variants. The Experiences API is read-only (list + retrieve).
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Errors" icon="triangle-exclamation" href="/developer/guides/errors">
    Every status code, what it means, and how to debug it.
  </Card>

  <Card title="Quickstart" icon="rocket" href="/developer/guides/quickstart">
    Apply these rules in a real product creation.
  </Card>

  <Card title="API reference" icon="code" href="/developer/api-reference/introduction">
    Per-endpoint parameters and schemas.
  </Card>

  <Card title="TypeScript SDK" icon="npm" href="/developer/guides/sdk">
    The SDK enforces most of these conventions for you.
  </Card>
</CardGroup>
