# Create and manage records

Create, retrieve, update, list, and delete custom object records with the API or your generated SDK.

A record is a single instance of an object definition. You create, retrieve, update, list, and delete records through the `/v2/extend/objects/<plural_name>` API or the SDK you generate from your definitions. To search records, use the API.

## Endpoints

Every object definition has the same set of record endpoints:

- **Create**: POST `/v2/extend/objects/<plural_name>`
- **Retrieve**: GET `/v2/extend/objects/<plural_name>/:id`
- **Update**: POST `/v2/extend/objects/<plural_name>/:id`
- **List**: GET `/v2/extend/objects/<plural_name>`
- **Search**: POST `/v2/extend/objects/<plural_name>/search` (see [Search records](https://docs.stripe.com/custom-objects/search.md))
- **Delete**: DELETE `/v2/extend/objects/<plural_name>/:id`

## Headers

Every request needs API version `2026-10-28.preview` or later in the `Stripe-Version` header. Every request except delete also names the version of your custom object definitions to use, in the `Stripe-Custom-Object-Versions` header, for example `Stripe-Custom-Object-Versions: support_tickets@0.0.1`. A request without it fails with a `version_required` error. See [Version your custom objects](https://docs.stripe.com/custom-objects/versioning.md). The examples on this page read your secret key from the `STRIPE_SECRET_KEY` environment variable. To call custom object endpoints with a [restricted API key](https://docs.stripe.com/keys/restricted-api-keys.md), give the key `Read` or `Write` access to **Object Records**, under **Extend**. Read access lets the key retrieve, list, and search records and read your object definitions. Write access also lets it create, update, and delete records.

## Create a record

Send the record’s fields in a `fields` object:

```bash
curl https://api.stripe.com/v2/extend/objects/support_tickets \
  -H "Authorization: Bearer $STRIPE_SECRET_KEY" \
  -H "Stripe-Version: 2026-10-28.preview" \
  -H "Stripe-Custom-Object-Versions: support_tickets@0.0.1" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "subject": "Order arrived damaged",
      "status": "open",
      "priority": 3,
      "customer": {"type": "customer", "id": "cus_abc123"}
    }
  }'
```

The response returns the record:

```json
{
  "id": "objrec_test_61RfVn0yAJiQAQHk7O16UQupaQVSQsnLcmkMmyS57AbC",
  "object": "v2.extend.objects.support_ticket",
  "object_definition": "objdef_test_61RfVmGz9KxtI81mu16UQupaQVSQsnLcmkMmyS57XyZ",
  "created": "2026-10-30T12:00:00.000Z",
  "livemode": false,
  "fields": {
    "subject": "Order arrived damaged",
    "status": "open",
    "priority": 3.0,
    "customer": {"type": "customer", "id": "cus_abc123", "url": "/v1/customers/cus_abc123"}
  }
}
```

Every record has:

- `id`: The record’s ID, which starts with `objrec_`.
- `object`: The record’s type, `v2.extend.objects.<singular_name>`.
- `object_definition`: The ID of the record’s object definition.
- `created`: When the record was created.
- `livemode`: Whether the record is in live mode.
- `fields`: Your data. Fields without a value return `null`, and references include a `url`, including references in array fields.

## Retrieve and list records

Retrieve a single record by ID, or list the records of an object definition, newest first. A list response includes `object: "v2.list"`, a `data` array, and pagination URLs. Lists don’t filter records. To filter or sort, use [search](https://docs.stripe.com/custom-objects/search.md).

A list returns 10 records by default. Set the `limit` query parameter to get up to 100. The `page` query parameter selects a page of results. To get the next page, send a request to `next_page_url` with the same headers instead of constructing a page token yourself. On the last page, `next_page_url` is `null`.

## Update a record

Updates are partial. In the `fields` object:

- A key with a value sets that field.
- A key set to `null` clears that field. You can’t clear a required field.
- A key you leave out keeps its current value.

```bash
curl https://api.stripe.com/v2/extend/objects/support_tickets/objrec_test_61RfVn0yAJiQAQHk7O16UQupaQVSQsnLcmkMmyS57AbC \
  -H "Authorization: Bearer $STRIPE_SECRET_KEY" \
  -H "Stripe-Version: 2026-10-28.preview" \
  -H "Stripe-Custom-Object-Versions: support_tickets@0.0.1" \
  -H "Content-Type: application/json" \
  -d '{"fields": {"status": "resolved"}}'
```

An update can only include fields that the requested version declares. Any other field fails with an `invalid_fields` error, even if its value is `null`. When you update an array field, the value you send replaces the whole array.

## Delete a record

Deleting a record removes it permanently. The response returns the record’s `id` and `object`, and later requests for the record return a `not_found` error. Delete requests don’t need the `Stripe-Custom-Object-Versions` header.

```bash
curl -X DELETE https://api.stripe.com/v2/extend/objects/support_tickets/objrec_test_61RfVn0yAJiQAQHk7O16UQupaQVSQsnLcmkMmyS57AbC \
  -H "Authorization: Bearer $STRIPE_SECRET_KEY" \
  -H "Stripe-Version: 2026-10-28.preview"
```

## Use the generated SDK

The generated Node.js SDK has typed `create`, `retrieve`, `update`, `list`, and `del` methods for each object definition. The example below includes the `Stripe-Custom-Object-Versions` header in the request options. For `retrieve` and `list`, pass an empty parameters object before the request options. Object names with more than one word use camelCase (`supportTickets`). For example, in a script in your app, such as `scripts/demo.mjs`:

```javascript
import SupportDesk from '../generated-sdk/node/cjs/stripe.cjs.node.js';

const client = new SupportDesk(process.env.STRIPE_SECRET_KEY, {
  apiVersion: '2026-10-28.preview',
  maxNetworkRetries: 0,
});
const versions = {
  headers: { 'Stripe-Custom-Object-Versions': 'support_tickets@0.0.1' },
};

const ticket = await client.v2.extend.objects.supportTickets.create(
  {
    fields: {
      subject: 'Order arrived damaged',
      status: 'open',
      priority: 3,
      customer: { type: 'customer', id: 'cus_abc123' },
    },
  },
  versions,
);

await client.v2.extend.objects.supportTickets.update(
  ticket.id,
  { fields: { status: 'resolved' } },
  versions,
);
const retrieved = await client.v2.extend.objects.supportTickets.retrieve(ticket.id, {}, versions);
const page = await client.v2.extend.objects.supportTickets.list({}, versions);
await client.v2.extend.objects.supportTickets.del(ticket.id);
```

The generated SDK retries some failed requests automatically. Creating a record doesn’t support idempotency keys, so a retry can create a duplicate record. This example sets `maxNetworkRetries: 0` to disable configurable retries, but the SDK can still retry one time after certain closed-connection errors. Store your own identifier in a field to reconcile an uncertain result. See [Private-preview limitations](https://docs.stripe.com/custom-objects/limitations.md).

## Field formats

Send and read each field type in these formats:

- **Datetime**: An ISO 8601 string in UTC with exactly three digits of fractional seconds, for example `2026-10-30T12:00:00.000Z`. Stripe rejects values without milliseconds, such as `2026-10-30T12:00:00Z`, and values with a time zone offset other than UTC.
- **Object reference**: An object with `type` and `id`, for example `{"type": "customer", "id": "cus_abc123"}`. For a custom object, `type` is `v2.extend.objects.<singular_name>`. Don’t send the `url` that read responses include.
- **Enum**: A value you defined, with matching case.
- **Integer**: A JSON number, not a string. Responses include a decimal part, such as `3.0`.
- **Decimal**: A string, for example `"12.50"`.
- **Currency**: A lowercase currency code, for example `usd`.
- **Monetary amount**: An object with `amount` in the currency’s minor unit and `currency`, for example `{"amount": "10000", "currency": "usd"}`.
- **Array**: A JSON array. Stripe keeps the order you send.
