# Custom objects use cases

See what you can build with custom objects, and learn when to use them.

Custom objects are general-purpose. You choose the objects, fields, and relationships, so you can use them in any industry and for any business model. The use cases on this page show patterns you can build with custom objects. They’re examples, not a complete list.

## When to use custom objects

Use custom objects when:

- **You run a separate database only to hold data about your Stripe data**: For example, a table keyed by customer or subscription IDs that exists only to add fields Stripe doesn’t have, or a copy of Stripe objects that you sync over webhooks only to link them to your own records.
- **Your business has things that aren’t Stripe objects**: [Metadata](https://docs.stripe.com/metadata.md) works for a few values on one object, such as an external ID or a label: up to 50 keys per object, with values of up to 500 characters, stored as strings. A custom object is a resource of its own, with typed and validated fields, references to Stripe objects and to your other records, events, and list and search endpoints.
- **Changes to your data should change something in Stripe**: A [workflow](https://docs.stripe.com/workflows.md) can start when a record is created or updated and then act on Stripe objects, for example by canceling the subscription or refunding the payment that the record references. A workflow can also create records when something happens in Stripe.
- **Your team uses a spreadsheet or an internal admin tool to see your data next to Stripe data**: Bring the data into Stripe and work with it in the Dashboard and in a [full-page app](https://docs.stripe.com/stripe-apps/patterns/full-page-apps.md) that you build for your team, instead of pulling Stripe data out.

You don’t have to move your system of record. You can keep your existing system and bring to Stripe only the data that your Stripe integration, automation, and teams need, or you can make custom objects the source of truth for that data. Either way, have one system write each field so the copies don’t drift apart. Use something else when:

- **A Stripe feature already covers it**: Check for a Stripe object or feature first.
- **You need a few values on one object**: Use [metadata](https://docs.stripe.com/metadata.md).
- **The data is a high-volume log, a time series, or bulk tabular data**: Keep it in your data warehouse.
- **The data is sensitive**: Don’t store sensitive information, such as payment card details, bank account details, government-issued ID numbers, passwords, or API keys, in custom objects.

## Hardware-based subscriptions

- **Problem**: You sell or lease hardware, such as sensors, meters, kiosks, or connected appliances, and bill for it with subscriptions. What a customer pays and what they’re entitled to depend on which devices are active, where they’re installed, and where each device is in its lifecycle. That information exists in a separate database or in subscription metadata that you reconcile with billing over webhooks.
- **Data model**: A device object with `serial_number` (the primary field), status (an enum: provisioned, active, suspended, returned), model, and references to the customer, the subscription, and a `service_site`. A `service_site` object with a name (the primary field), a site code, and a reference to the customer.
- **How it works**: Your provisioning system creates and updates device records through the API. A workflow starts when a device is updated and, if its status is returned, updates or cancels the referenced subscription. Your service team filters devices by status in the Dashboard, and a private app shows each customer’s devices on the customer’s page.
- **Why on Stripe**: Device state and subscription state live in the same account, so billing changes follow the device lifecycle without a sync layer, and your support team sees hardware and billing in one place.

## Reservations and bookings

- **Problem**: Hotels, vacation rentals, tour operators, and rental businesses sell reservations with deposits, cancellation policies, incidental charges, and guest preferences. Stripe holds the payments, but reservation details live in a booking engine or property management system, so your finance team can’t see reservation details when it reconciles payments, refunds, and payouts.
- **Data model**: A reservation object with `confirmation_code` (the primary field), status (an enum: booked, `checked_in`, `checked_out`, canceled), check-in and check-out datetimes, `guest_count`, total (a monetary amount), and references to the customer, a property, and the deposit’s PaymentIntent. A `guest_profile` object with a profile label (the primary field), a loyalty tier, room preferences, and a reference to the customer.
- **How it works**: Your booking engine creates the reservation through the API after the guest pays the deposit. A workflow starts when a reservation is updated and, if its status is canceled, refunds the referenced deposit. A private app shows a guest’s upcoming stays and preferences on the customer’s page. Your finance team filters reservations by property or status in the Dashboard.
- **Why on Stripe**: Reservation details sit next to the payments and invoices they explain. You can keep your property management system as the system of record and bring over only the fields you need in Stripe.

## Shipments and fulfillment

- **Problem**: You track shipments, carriers, and delivery status in a separate system linked to Stripe with metadata, so your team can’t see a shipment next to the invoice or payment it fulfills.
- **Data model**: A shipment object with `tracking_number` (the primary field), carrier, status (an enum: `in_transit`, delivered, exception), `shipping_cost` (a monetary amount), shipped and delivered datetimes, and references to the customer and to the invoice the shipment fulfills.
- **How it works**: Your fulfillment integration updates shipments through the API as carrier updates arrive. A workflow starts when a shipment is updated and, if its status is exception, emails your team. Your support team finds a customer’s shipments in the Dashboard, and a private app can show them on the customer’s page.
- **Why on Stripe**: Fulfillment status sits next to the invoice or payment it belongs to, so your team can answer “Where is my order?” from one place, and shipments become records you can list and filter instead of a metadata convention.

## Customer requests and follow-ups

- **Problem**: Requests such as cancellations, refund requests, and goodwill credits have their own lifecycle, owner, reason, and outcome, and they lead to changes in Stripe. Teams track them in spreadsheets, in metadata, or in a separate ticketing tool, and then make the Stripe change by hand.
- **Data model**: A `customer_request` object with `request_number` (the primary field), type (an enum: cancellation, refund, credit), status (an enum: open, `in_review`, resolved, declined), reason, `requested_amount` (a monetary amount), and references to the customer, subscription, and payment that the request is about.
- **How it works**: Your support tool, your app, or an AI agent creates requests through the API. Your team works through the queue in a full-page app. A workflow starts when a request is updated and, if its status is resolved, makes the change in Stripe, for example by canceling the referenced subscription.
- **Why on Stripe**: Each request, its reason, and its outcome stay attached to the customer, subscription, or payment they affect, and your team can filter past requests by type, status, or customer.

## Configuration data for your Stripe integration

- **Problem**: Your billing and invoicing logic depends on lookup tables specific to your business, such as invoice text and tax labels for each country, branch attributes for reporting, or partner terms. They live in config files, spreadsheets, or a small database, apart from the Stripe data they govern.
- **Data model**: A `country_setting` object with `country_code` (the primary field), `invoice_footer`, `tax_label`, `vat_rate` (a decimal), and `default_currency` (a currency).
- **How it works**: Your team maintains the records through the API or a private app. Your integration searches for the matching record by country code when it creates invoices, and your reporting jobs read the records through the API.
- **Why on Stripe**: Configuration lives next to the Stripe data it governs, with typed and validated fields and a schema you version as it changes.

## For app developers: your app’s own records

- **Problem**: If you publish an app on the [Stripe App Marketplace](https://docs.stripe.com/stripe-apps/publish-app.md), you also run the business behind it: installations, the plan each installing business is on, onboarding status, and support cases. That data usually lives in your own database, separate from the Stripe customers and subscriptions you bill those businesses with.
- **Data model**: An `app_installation` object with the installing account’s ID (the primary field), status (an enum: trial, active, uninstalled), plan, and references to the customer and subscription you bill.
- **How it works**: Define these objects in a [private app](https://docs.stripe.com/stripe-apps/distribution-options.md) in your own account, and have your app’s back end read and write the records through the API. Your team views installations in your Dashboard and updates them through the private app or the API. During private preview, custom objects aren’t supported in public apps, so don’t include custom object definitions in the app you publish.
- **Why on Stripe**: Your app’s business data sits next to the customers and subscriptions you use to bill for the app.
