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

# Metadata Guide

> Use metadata to store your own key-value data, such as order IDs and CRM references, on Dodo Payments payments, subscriptions, customers, and more.

## Introduction

Metadata lets you store your own key-value data on Dodo Payments objects, such as an order ID from your system or a CRM reference. You can attach metadata to most objects, including payments, subscriptions, customers, and products. See [Supported Objects](#supported-objects) for the full list.

## Overview

Metadata follows these rules:

* Metadata keys can be up to 40 characters long (up to 100 characters for usage events ingested through `POST /events/ingest`).
* Metadata values can be a string, integer, number, or boolean. String values can be up to 500 characters long.
* Objects, arrays, and `null` are not accepted as metadata values.
* You can add up to 50 metadata key-value pairs per object. A request with more returns the `MAXIMUM_KEYS_REACHED` [error code](/api-reference/error-codes).
* The API can't search or filter by metadata, but it returns metadata in API responses and webhooks.

## Use Cases

Use metadata to:

* Store external IDs or references.
* Add internal notes.
* Link Dodo Payments objects to records in your system.
* Categorize transactions.
* Add custom attributes for reporting.

## Adding Metadata

Add metadata when you create or update an object through the API. For products, you can also add metadata in the dashboard.

### Via API

Pass a `metadata` object in the request body. The examples below use the TypeScript SDK and assume an initialized `client`:

```javascript expandable theme={null}
// Assumes an initialized client, for example:
// const client = new DodoPayments({ bearerToken: process.env.DODO_PAYMENTS_API_KEY });

// Adding metadata when creating a checkout session
const checkoutSession = await client.checkoutSessions.create({
    product_cart: [{ product_id: 'pdt_123', quantity: 1 }],
    return_url: 'https://example.com/return',
    metadata: {
        order_id: 'ORD-123',
        campaign_source: 'email',
        customer_segment: 'premium'
    }
});

// Adding metadata when creating a payment
// Note: POST /payments is deprecated. Use checkout sessions for new integrations
const payment = await client.payments.create({
    billing: { city: 'city', country: 'AF', state: 'state', street: 'street', zipcode: '12345' },
    customer: { customer_id: 'cus_123' },
    product_cart: [{ product_id: 'pdt_123', quantity: 0 }],
    metadata:{order_id: 'ORD-123', customer_notes: 'Customer notes'}
  });

// Adding metadata when creating a product
const product = await client.products.create({
    name: 'Premium Software License',
    tax_category: 'digital_products',
    price: {
        type: 'one_time_price',
        currency: 'USD',
        price: 9900,
        discount_bps: 0,
        purchasing_power_parity: false
    },
    metadata: {
        category: 'software',
        license_type: 'premium',
        support_tier: 'priority'
    }
});

// Adding metadata when creating a customer
const customer = await client.customers.create({
    email: 'customer@example.com',
    name: 'John Doe',
    metadata: {
        crm_id: 'CRM-12345',
        customer_segment: 'enterprise',
        referral_source: 'website'
    }
});

// Adding metadata when changing a subscription plan
await client.subscriptions.changePlan('sub_123', {
    product_id: 'pdt_premium',
    proration_billing_mode: 'prorated_immediately',
    quantity: 1,
    metadata: {
        upgrade_reason: 'feature_request',
        previous_plan: 'basic',
        sales_rep: 'john@example.com'
    }
});

// Adding metadata when creating a discount
const discount = await client.discounts.create({
    type: 'percentage',
    amount: 1500, // 15%
    code: 'SUMMER2025',
    metadata: {
        campaign: 'summer_promo',
        source: 'email_blast',
        team: 'marketing'
    }
});
```

### Via Dashboard UI (Products Only)

To add metadata to a product without writing code, open the product in **Products** and add key-value pairs in the metadata section. You can do this when you create or edit the product.

<Frame>
  <img src="https://mintcdn.com/dodopayments/5D2vY2CKcFOZLLyz/images/product-catalog/product-metadata-ui.png?fit=max&auto=format&n=5D2vY2CKcFOZLLyz&q=85&s=9e4fee91d8922325d8c1c72b27ccb0a2" alt="Product metadata section in the Dodo Payments dashboard" style={{ maxHeight: '500px', width: 'auto' }} width="2156" height="420" data-path="images/product-catalog/product-metadata-ui.png" />
</Frame>

<Tip>
  Team members who don't work with the API can use the dashboard to manage product metadata, such as product categories.
</Tip>

## Retrieving Metadata

API responses include metadata when you retrieve an object:

```javascript theme={null}
// Retrieving payment metadata
const payment = await client.payments.retrieve('pay_123');
console.log(payment.metadata.order_id); // 'ORD-123'

// Retrieving customer metadata
const customer = await client.customers.retrieve('cus_123');
console.log(customer.metadata.crm_id); // 'CRM-12345'

// Retrieving refund metadata
const refund = await client.refunds.retrieve('ref_123');
console.log(refund.metadata.refund_reason); // 'customer_request'
```

<Note>
  Retrieving a checkout session (`GET /checkouts/{id}`) doesn't return `metadata`. The session status response contains only `id`, `created_at`, `payment_id`, `payment_status`, `customer_email`, and `customer_name`. To read the metadata you attached when you created the session, retrieve the resulting payment with the returned `payment_id`.
</Note>

## Searching and Filtering

The API can't search by metadata. To find an object by a metadata value:

1. Store your important identifiers in metadata.
2. List or retrieve objects through the API.
3. Filter the results in your application code.

```javascript theme={null}
// Example: Find a payment using your order ID.
// list() is auto-paginating, so iterating searches across pages instead of
// only the first one.
let matchingPayment;
for await (const payment of client.payments.list({ page_size: 100 })) {
  if (payment.metadata?.order_id === '6735') {
    matchingPayment = payment;
    break;
  }
}
```

## Best Practices

Follow these guidelines to keep metadata useful.

### Do:

* Use consistent naming conventions for metadata keys.
* Document your metadata schema internally.
* Keep values short and meaningful.
* Use metadata for static data only.
* Consider prefixes that name the source system, for example `crm_id` or `inventory_sku`.

### Don't:

* Store sensitive data in metadata.
* Use metadata for values that change often.
* Rely on metadata for critical business logic.
* Duplicate information that the object already contains.
* Use special characters in metadata keys.

## Supported Objects

These objects support metadata:

| Object Type | Support |
| - | - |
| Payments | ✓ |
| Subscriptions | ✓ |
| Subscription Change Plan | ✓ |
| Subscription Charges | ✓ |
| Products | ✓ |
| Refunds | ✓ |
| Checkout Sessions | ✓ |
| Customers | ✓ |
| Discounts | ✓ |
| Entitlements | ✓ |
| Credit Ledger Entries | ✓ |
| Usage Events | ✓ |
| Webhooks | ✓ |

## Webhooks and Metadata

Webhook payloads include the metadata of the object, so your webhook handler can match an event to your own records:

```javascript theme={null}
// Example webhook handler. Assumes an Express `app` that parses JSON bodies.
// Verify the webhook signature before you trust the payload:
// see /developer-resources/webhooks
app.post('/webhook', (req, res) => {
  const event = req.body;

  if (event.type === 'payment.succeeded') {
    const orderId = event.data.metadata.order_id;
    // Process order using your internal order ID
  }
  
  if (event.type === 'subscription.active') {
    const orderId = event.data.metadata.order_id;
    const campaignSource = event.data.metadata.campaign_source;
    // Handle subscription activation with custom metadata
  }

  res.sendStatus(200);
});
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.