#
tokens: 49110/50000 19/260 files (page 4/8)
lines: off (toggle) GitHub
raw markdown copy
This is page 4 of 8. Use http://codebase.md/dodopayments/dodopayments-node?page={x} to view the full context.

# Directory Structure

```
├── .devcontainer
│   └── devcontainer.json
├── .github
│   └── workflows
│       ├── ci.yml
│       ├── docker-mcp.yml
│       ├── publish-npm.yml
│       └── release-doctor.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.json
├── .release-please-manifest.json
├── .stats.yml
├── api.md
├── bin
│   ├── check-release-environment
│   ├── cli
│   ├── docker-tags
│   ├── migration-config.json
│   └── publish-npm
├── Brewfile
├── CHANGELOG.md
├── CONTRIBUTING.md
├── eslint.config.mjs
├── examples
│   └── .keep
├── jest.config.ts
├── LICENSE
├── MIGRATION.md
├── package.json
├── packages
│   └── mcp-server
│       ├── .dockerignore
│       ├── build
│       ├── cloudflare-worker
│       │   ├── .gitignore
│       │   ├── biome.json
│       │   ├── package.json
│       │   ├── README.md
│       │   ├── src
│       │   │   ├── app.ts
│       │   │   ├── index.ts
│       │   │   └── utils.ts
│       │   ├── static
│       │   │   └── home.md
│       │   ├── tsconfig.json
│       │   ├── worker-configuration.d.ts
│       │   └── wrangler.jsonc
│       ├── Dockerfile
│       ├── jest.config.ts
│       ├── manifest.json
│       ├── package.json
│       ├── README.md
│       ├── scripts
│       │   ├── copy-bundle-files.cjs
│       │   └── postprocess-dist-package-json.cjs
│       ├── src
│       │   ├── code-tool-paths.cts
│       │   ├── code-tool-types.ts
│       │   ├── code-tool-worker.ts
│       │   ├── code-tool.ts
│       │   ├── compat.ts
│       │   ├── docs-search-tool.ts
│       │   ├── dynamic-tools.ts
│       │   ├── filtering.ts
│       │   ├── headers.ts
│       │   ├── http.ts
│       │   ├── index.ts
│       │   ├── options.ts
│       │   ├── server.ts
│       │   ├── stdio.ts
│       │   ├── tools
│       │   │   ├── addons
│       │   │   │   ├── create-addons.ts
│       │   │   │   ├── list-addons.ts
│       │   │   │   ├── retrieve-addons.ts
│       │   │   │   ├── update-addons.ts
│       │   │   │   └── update-images-addons.ts
│       │   │   ├── brands
│       │   │   │   ├── create-brands.ts
│       │   │   │   ├── list-brands.ts
│       │   │   │   ├── retrieve-brands.ts
│       │   │   │   ├── update-brands.ts
│       │   │   │   └── update-images-brands.ts
│       │   │   ├── checkout-sessions
│       │   │   │   ├── create-checkout-sessions.ts
│       │   │   │   └── retrieve-checkout-sessions.ts
│       │   │   ├── customers
│       │   │   │   ├── create-customers.ts
│       │   │   │   ├── customer-portal
│       │   │   │   │   └── create-customers-customer-portal.ts
│       │   │   │   ├── list-customers.ts
│       │   │   │   ├── retrieve-customers.ts
│       │   │   │   ├── update-customers.ts
│       │   │   │   └── wallets
│       │   │   │       ├── ledger-entries
│       │   │   │       │   ├── create-wallets-customers-ledger-entries.ts
│       │   │   │       │   └── list-wallets-customers-ledger-entries.ts
│       │   │   │       └── list-customers-wallets.ts
│       │   │   ├── discounts
│       │   │   │   ├── create-discounts.ts
│       │   │   │   ├── delete-discounts.ts
│       │   │   │   ├── list-discounts.ts
│       │   │   │   ├── retrieve-discounts.ts
│       │   │   │   └── update-discounts.ts
│       │   │   ├── disputes
│       │   │   │   ├── list-disputes.ts
│       │   │   │   └── retrieve-disputes.ts
│       │   │   ├── index.ts
│       │   │   ├── invoices
│       │   │   │   └── payments
│       │   │   │       ├── retrieve-invoices-payments.ts
│       │   │   │       └── retrieve-refund-invoices-payments.ts
│       │   │   ├── license-key-instances
│       │   │   │   ├── list-license-key-instances.ts
│       │   │   │   ├── retrieve-license-key-instances.ts
│       │   │   │   └── update-license-key-instances.ts
│       │   │   ├── license-keys
│       │   │   │   ├── list-license-keys.ts
│       │   │   │   ├── retrieve-license-keys.ts
│       │   │   │   └── update-license-keys.ts
│       │   │   ├── licenses
│       │   │   │   ├── activate-licenses.ts
│       │   │   │   ├── deactivate-licenses.ts
│       │   │   │   └── validate-licenses.ts
│       │   │   ├── meters
│       │   │   │   ├── archive-meters.ts
│       │   │   │   ├── create-meters.ts
│       │   │   │   ├── list-meters.ts
│       │   │   │   ├── retrieve-meters.ts
│       │   │   │   └── unarchive-meters.ts
│       │   │   ├── misc
│       │   │   │   └── list-supported-countries-misc.ts
│       │   │   ├── payments
│       │   │   │   ├── create-payments.ts
│       │   │   │   ├── list-payments.ts
│       │   │   │   ├── retrieve-line-items-payments.ts
│       │   │   │   └── retrieve-payments.ts
│       │   │   ├── payouts
│       │   │   │   └── list-payouts.ts
│       │   │   ├── products
│       │   │   │   ├── archive-products.ts
│       │   │   │   ├── create-products.ts
│       │   │   │   ├── images
│       │   │   │   │   └── update-products-images.ts
│       │   │   │   ├── list-products.ts
│       │   │   │   ├── retrieve-products.ts
│       │   │   │   ├── unarchive-products.ts
│       │   │   │   ├── update-files-products.ts
│       │   │   │   └── update-products.ts
│       │   │   ├── refunds
│       │   │   │   ├── create-refunds.ts
│       │   │   │   ├── list-refunds.ts
│       │   │   │   └── retrieve-refunds.ts
│       │   │   ├── subscriptions
│       │   │   │   ├── change-plan-subscriptions.ts
│       │   │   │   ├── charge-subscriptions.ts
│       │   │   │   ├── create-subscriptions.ts
│       │   │   │   ├── list-subscriptions.ts
│       │   │   │   ├── retrieve-subscriptions.ts
│       │   │   │   ├── retrieve-usage-history-subscriptions.ts
│       │   │   │   └── update-subscriptions.ts
│       │   │   ├── types.ts
│       │   │   ├── usage-events
│       │   │   │   ├── ingest-usage-events.ts
│       │   │   │   ├── list-usage-events.ts
│       │   │   │   └── retrieve-usage-events.ts
│       │   │   └── webhooks
│       │   │       ├── create-webhooks.ts
│       │   │       ├── delete-webhooks.ts
│       │   │       ├── headers
│       │   │       │   ├── retrieve-webhooks-headers.ts
│       │   │       │   └── update-webhooks-headers.ts
│       │   │       ├── list-webhooks.ts
│       │   │       ├── retrieve-secret-webhooks.ts
│       │   │       ├── retrieve-webhooks.ts
│       │   │       └── update-webhooks.ts
│       │   └── tools.ts
│       ├── tests
│       │   ├── compat.test.ts
│       │   ├── dynamic-tools.test.ts
│       │   ├── options.test.ts
│       │   └── tools.test.ts
│       ├── tsc-multi.json
│       ├── tsconfig.build.json
│       ├── tsconfig.dist-src.json
│       ├── tsconfig.json
│       └── yarn.lock
├── README.md
├── release-please-config.json
├── scripts
│   ├── bootstrap
│   ├── build
│   ├── build-all
│   ├── fast-format
│   ├── format
│   ├── lint
│   ├── mock
│   ├── publish-packages.ts
│   ├── test
│   └── utils
│       ├── attw-report.cjs
│       ├── check-is-in-git-install.sh
│       ├── check-version.cjs
│       ├── fix-index-exports.cjs
│       ├── git-swap.sh
│       ├── make-dist-package-json.cjs
│       ├── postprocess-files.cjs
│       └── upload-artifact.sh
├── SECURITY.md
├── src
│   ├── api-promise.ts
│   ├── client.ts
│   ├── core
│   │   ├── api-promise.ts
│   │   ├── error.ts
│   │   ├── pagination.ts
│   │   ├── README.md
│   │   ├── resource.ts
│   │   └── uploads.ts
│   ├── error.ts
│   ├── index.ts
│   ├── internal
│   │   ├── builtin-types.ts
│   │   ├── detect-platform.ts
│   │   ├── errors.ts
│   │   ├── headers.ts
│   │   ├── parse.ts
│   │   ├── README.md
│   │   ├── request-options.ts
│   │   ├── shim-types.ts
│   │   ├── shims.ts
│   │   ├── to-file.ts
│   │   ├── types.ts
│   │   ├── uploads.ts
│   │   ├── utils
│   │   │   ├── base64.ts
│   │   │   ├── bytes.ts
│   │   │   ├── env.ts
│   │   │   ├── log.ts
│   │   │   ├── path.ts
│   │   │   ├── sleep.ts
│   │   │   ├── uuid.ts
│   │   │   └── values.ts
│   │   └── utils.ts
│   ├── lib
│   │   └── .keep
│   ├── pagination.ts
│   ├── resource.ts
│   ├── resources
│   │   ├── addons.ts
│   │   ├── brands.ts
│   │   ├── checkout-sessions.ts
│   │   ├── customers
│   │   │   ├── customer-portal.ts
│   │   │   ├── customers.ts
│   │   │   ├── index.ts
│   │   │   ├── wallets
│   │   │   │   ├── index.ts
│   │   │   │   ├── ledger-entries.ts
│   │   │   │   └── wallets.ts
│   │   │   └── wallets.ts
│   │   ├── customers.ts
│   │   ├── discounts.ts
│   │   ├── disputes.ts
│   │   ├── index.ts
│   │   ├── invoices
│   │   │   ├── index.ts
│   │   │   ├── invoices.ts
│   │   │   └── payments.ts
│   │   ├── invoices.ts
│   │   ├── license-key-instances.ts
│   │   ├── license-keys.ts
│   │   ├── licenses.ts
│   │   ├── meters.ts
│   │   ├── misc.ts
│   │   ├── payments.ts
│   │   ├── payouts.ts
│   │   ├── products
│   │   │   ├── images.ts
│   │   │   ├── index.ts
│   │   │   └── products.ts
│   │   ├── products.ts
│   │   ├── refunds.ts
│   │   ├── subscriptions.ts
│   │   ├── usage-events.ts
│   │   ├── webhook-events.ts
│   │   ├── webhooks
│   │   │   ├── headers.ts
│   │   │   ├── index.ts
│   │   │   └── webhooks.ts
│   │   └── webhooks.ts
│   ├── resources.ts
│   ├── uploads.ts
│   └── version.ts
├── tests
│   ├── api-resources
│   │   ├── addons.test.ts
│   │   ├── brands.test.ts
│   │   ├── checkout-sessions.test.ts
│   │   ├── customers
│   │   │   ├── customer-portal.test.ts
│   │   │   ├── customers.test.ts
│   │   │   └── wallets
│   │   │       ├── ledger-entries.test.ts
│   │   │       └── wallets.test.ts
│   │   ├── discounts.test.ts
│   │   ├── disputes.test.ts
│   │   ├── license-key-instances.test.ts
│   │   ├── license-keys.test.ts
│   │   ├── licenses.test.ts
│   │   ├── meters.test.ts
│   │   ├── misc.test.ts
│   │   ├── payments.test.ts
│   │   ├── payouts.test.ts
│   │   ├── products
│   │   │   ├── images.test.ts
│   │   │   └── products.test.ts
│   │   ├── refunds.test.ts
│   │   ├── subscriptions.test.ts
│   │   ├── usage-events.test.ts
│   │   └── webhooks
│   │       ├── headers.test.ts
│   │       └── webhooks.test.ts
│   ├── base64.test.ts
│   ├── buildHeaders.test.ts
│   ├── form.test.ts
│   ├── index.test.ts
│   ├── path.test.ts
│   ├── stringifyQuery.test.ts
│   └── uploads.test.ts
├── tsc-multi.json
├── tsconfig.build.json
├── tsconfig.deno.json
├── tsconfig.dist-src.json
├── tsconfig.json
└── yarn.lock
```

# Files

--------------------------------------------------------------------------------
/packages/mcp-server/tests/dynamic-tools.test.ts:
--------------------------------------------------------------------------------

```typescript
import { dynamicTools } from '../src/dynamic-tools';
import { Endpoint } from '../src/tools';

describe('dynamicTools', () => {
  const fakeClient = {} as any;

  const endpoints: Endpoint[] = [
    makeEndpoint('test_read_endpoint', 'test_resource', 'read', ['test']),
    makeEndpoint('test_write_endpoint', 'test_resource', 'write', ['test']),
    makeEndpoint('user_endpoint', 'user', 'read', ['user', 'admin']),
    makeEndpoint('admin_endpoint', 'admin', 'write', ['admin']),
  ];

  const tools = dynamicTools(endpoints);

  const toolsMap = {
    list_api_endpoints: toolOrError('list_api_endpoints'),
    get_api_endpoint_schema: toolOrError('get_api_endpoint_schema'),
    invoke_api_endpoint: toolOrError('invoke_api_endpoint'),
  };

  describe('list_api_endpoints', () => {
    it('should return all endpoints when no search query is provided', async () => {
      const content = await toolsMap.list_api_endpoints.handler(fakeClient, {});
      const result = JSON.parse(content.content[0].text);

      expect(result.tools).toHaveLength(endpoints.length);
      expect(result.tools.map((t: { name: string }) => t.name)).toContain('test_read_endpoint');
      expect(result.tools.map((t: { name: string }) => t.name)).toContain('test_write_endpoint');
      expect(result.tools.map((t: { name: string }) => t.name)).toContain('user_endpoint');
      expect(result.tools.map((t: { name: string }) => t.name)).toContain('admin_endpoint');
    });

    it('should filter endpoints by name', async () => {
      const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'user' });
      const result = JSON.parse(content.content[0].text);

      expect(result.tools).toHaveLength(1);
      expect(result.tools[0].name).toBe('user_endpoint');
    });

    it('should filter endpoints by resource', async () => {
      const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'admin' });
      const result = JSON.parse(content.content[0].text);

      expect(result.tools.some((t: { resource: string }) => t.resource === 'admin')).toBeTruthy();
    });

    it('should filter endpoints by tag', async () => {
      const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'admin' });
      const result = JSON.parse(content.content[0].text);

      expect(result.tools.some((t: { tags: string[] }) => t.tags.includes('admin'))).toBeTruthy();
    });

    it('should be case insensitive in search', async () => {
      const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'ADMIN' });
      const result = JSON.parse(content.content[0].text);

      expect(result.tools.length).toBe(2);
      result.tools.forEach((tool: { name: string; resource: string; tags: string[] }) => {
        expect(
          tool.name.toLowerCase().includes('admin') ||
            tool.resource.toLowerCase().includes('admin') ||
            tool.tags.some((tag: string) => tag.toLowerCase().includes('admin')),
        ).toBeTruthy();
      });
    });

    it('should filter endpoints by description', async () => {
      const content = await toolsMap.list_api_endpoints.handler(fakeClient, {
        search_query: 'Test endpoint for user_endpoint',
      });
      const result = JSON.parse(content.content[0].text);

      expect(result.tools).toHaveLength(1);
      expect(result.tools[0].name).toBe('user_endpoint');
      expect(result.tools[0].description).toBe('Test endpoint for user_endpoint');
    });

    it('should filter endpoints by partial description match', async () => {
      const content = await toolsMap.list_api_endpoints.handler(fakeClient, {
        search_query: 'endpoint for user',
      });
      const result = JSON.parse(content.content[0].text);

      expect(result.tools).toHaveLength(1);
      expect(result.tools[0].name).toBe('user_endpoint');
    });
  });

  describe('get_api_endpoint_schema', () => {
    it('should return schema for existing endpoint', async () => {
      const content = await toolsMap.get_api_endpoint_schema.handler(fakeClient, {
        endpoint: 'test_read_endpoint',
      });
      const result = JSON.parse(content.content[0].text);

      expect(result).toEqual(endpoints[0]?.tool);
    });

    it('should throw error for non-existent endpoint', async () => {
      await expect(
        toolsMap.get_api_endpoint_schema.handler(fakeClient, { endpoint: 'non_existent_endpoint' }),
      ).rejects.toThrow('Endpoint non_existent_endpoint not found');
    });

    it('should throw error when no endpoint provided', async () => {
      await expect(toolsMap.get_api_endpoint_schema.handler(fakeClient, undefined)).rejects.toThrow(
        'No endpoint provided',
      );
    });
  });

  describe('invoke_api_endpoint', () => {
    it('should successfully invoke endpoint with valid arguments', async () => {
      const mockHandler = endpoints[0]?.handler as jest.Mock;
      mockHandler.mockClear();

      await toolsMap.invoke_api_endpoint.handler(fakeClient, {
        endpoint_name: 'test_read_endpoint',
        args: { testParam: 'test value' },
      });

      expect(mockHandler).toHaveBeenCalledWith(fakeClient, { testParam: 'test value' });
    });

    it('should throw error for non-existent endpoint', async () => {
      await expect(
        toolsMap.invoke_api_endpoint.handler(fakeClient, {
          endpoint_name: 'non_existent_endpoint',
          args: { testParam: 'test value' },
        }),
      ).rejects.toThrow(/Endpoint non_existent_endpoint not found/);
    });

    it('should throw error when no arguments provided', async () => {
      await expect(toolsMap.invoke_api_endpoint.handler(fakeClient, undefined)).rejects.toThrow(
        'No endpoint provided',
      );
    });

    it('should throw error for invalid argument schema', async () => {
      await expect(
        toolsMap.invoke_api_endpoint.handler(fakeClient, {
          endpoint_name: 'test_read_endpoint',
          args: { wrongParam: 'test value' }, // Missing required testParam
        }),
      ).rejects.toThrow(/Invalid arguments for endpoint/);
    });
  });

  function toolOrError(name: string) {
    const tool = tools.find((tool) => tool.tool.name === name);
    if (!tool) throw new Error(`Tool ${name} not found`);
    return tool;
  }
});

function makeEndpoint(
  name: string,
  resource: string,
  operation: 'read' | 'write',
  tags: string[] = [],
): Endpoint {
  return {
    metadata: {
      resource,
      operation,
      tags,
    },
    tool: {
      name,
      description: `Test endpoint for ${name}`,
      inputSchema: {
        type: 'object',
        properties: {
          testParam: { type: 'string' },
        },
        required: ['testParam'],
      },
    },
    handler: jest.fn().mockResolvedValue({ success: true }),
  };
}

```

--------------------------------------------------------------------------------
/src/resources/usage-events.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { APIResource } from '../core/resource';
import { APIPromise } from '../core/api-promise';
import {
  DefaultPageNumberPagination,
  type DefaultPageNumberPaginationParams,
  PagePromise,
} from '../core/pagination';
import { RequestOptions } from '../internal/request-options';
import { path } from '../internal/utils/path';

export class UsageEvents extends APIResource {
  /**
   * Fetch detailed information about a single event using its unique event ID. This
   * endpoint is useful for:
   *
   * - Debugging specific event ingestion issues
   * - Retrieving event details for customer support
   * - Validating that events were processed correctly
   * - Getting the complete metadata for an event
   *
   * ## Event ID Format:
   *
   * The event ID should be the same value that was provided during event ingestion
   * via the `/events/ingest` endpoint. Event IDs are case-sensitive and must match
   * exactly.
   *
   * ## Response Details:
   *
   * The response includes all event data including:
   *
   * - Complete metadata key-value pairs
   * - Original timestamp (preserved from ingestion)
   * - Customer and business association
   * - Event name and processing information
   *
   * ## Example Usage:
   *
   * ```text
   * GET /events/api_call_12345
   * ```
   */
  retrieve(eventID: string, options?: RequestOptions): APIPromise<Event> {
    return this._client.get(path`/events/${eventID}`, options);
  }

  /**
   * Fetch events from your account with powerful filtering capabilities. This
   * endpoint is ideal for:
   *
   * - Debugging event ingestion issues
   * - Analyzing customer usage patterns
   * - Building custom analytics dashboards
   * - Auditing billing-related events
   *
   * ## Filtering Options:
   *
   * - **Customer filtering**: Filter by specific customer ID
   * - **Event name filtering**: Filter by event type/name
   * - **Meter-based filtering**: Use a meter ID to apply the meter's event name and
   *   filter criteria automatically
   * - **Time range filtering**: Filter events within a specific date range
   * - **Pagination**: Navigate through large result sets
   *
   * ## Meter Integration:
   *
   * When using `meter_id`, the endpoint automatically applies:
   *
   * - The meter's configured `event_name` filter
   * - The meter's custom filter criteria (if any)
   * - If you also provide `event_name`, it must match the meter's event name
   *
   * ## Example Queries:
   *
   * - Get all events for a customer: `?customer_id=cus_abc123`
   * - Get API request events: `?event_name=api_request`
   * - Get events from last 24 hours:
   *   `?start=2024-01-14T10:30:00Z&end=2024-01-15T10:30:00Z`
   * - Get events with meter filtering: `?meter_id=mtr_xyz789`
   * - Paginate results: `?page_size=50&page_number=2`
   */
  list(
    query: UsageEventListParams | null | undefined = {},
    options?: RequestOptions,
  ): PagePromise<EventsDefaultPageNumberPagination, Event> {
    return this._client.getAPIList('/events', DefaultPageNumberPagination<Event>, { query, ...options });
  }

  /**
   * This endpoint allows you to ingest custom events that can be used for:
   *
   * - Usage-based billing and metering
   * - Analytics and reporting
   * - Customer behavior tracking
   *
   * ## Important Notes:
   *
   * - **Duplicate Prevention**:
   *   - Duplicate `event_id` values within the same request are rejected (entire
   *     request fails)
   *   - Subsequent requests with existing `event_id` values are ignored (idempotent
   *     behavior)
   * - **Rate Limiting**: Maximum 1000 events per request
   * - **Time Validation**: Events with timestamps older than 1 hour or more than 5
   *   minutes in the future will be rejected
   * - **Metadata Limits**: Maximum 50 key-value pairs per event, keys max 100 chars,
   *   values max 500 chars
   *
   * ## Example Usage:
   *
   * ```json
   * {
   *   "events": [
   *     {
   *       "event_id": "api_call_12345",
   *       "customer_id": "cus_abc123",
   *       "event_name": "api_request",
   *       "timestamp": "2024-01-15T10:30:00Z",
   *       "metadata": {
   *         "endpoint": "/api/v1/users",
   *         "method": "GET",
   *         "tokens_used": "150"
   *       }
   *     }
   *   ]
   * }
   * ```
   */
  ingest(body: UsageEventIngestParams, options?: RequestOptions): APIPromise<UsageEventIngestResponse> {
    return this._client.post('/events/ingest', { body, ...options });
  }
}

export type EventsDefaultPageNumberPagination = DefaultPageNumberPagination<Event>;

export interface Event {
  business_id: string;

  customer_id: string;

  event_id: string;

  event_name: string;

  timestamp: string;

  /**
   * Arbitrary key-value metadata. Values can be string, integer, number, or boolean.
   */
  metadata?: { [key: string]: string | number | boolean } | null;
}

export interface EventInput {
  /**
   * customer_id of the customer whose usage needs to be tracked
   */
  customer_id: string;

  /**
   * Event Id acts as an idempotency key. Any subsequent requests with the same
   * event_id will be ignored
   */
  event_id: string;

  /**
   * Name of the event
   */
  event_name: string;

  /**
   * Custom metadata. Only key value pairs are accepted, objects or arrays submitted
   * will be rejected.
   */
  metadata?: { [key: string]: string | number | boolean } | null;

  /**
   * Custom Timestamp. Defaults to current timestamp in UTC. Timestamps that are
   * older that 1 hour or after 5 mins, from current timestamp, will be rejected.
   */
  timestamp?: string | null;
}

export interface UsageEventIngestResponse {
  ingested_count: number;
}

export interface UsageEventListParams extends DefaultPageNumberPaginationParams {
  /**
   * Filter events by customer ID
   */
  customer_id?: string;

  /**
   * Filter events created before this timestamp
   */
  end?: string;

  /**
   * Filter events by event name. If both event_name and meter_id are provided, they
   * must match the meter's configured event_name
   */
  event_name?: string;

  /**
   * Filter events by meter ID. When provided, only events that match the meter's
   * event_name and filter criteria will be returned
   */
  meter_id?: string;

  /**
   * Filter events created after this timestamp
   */
  start?: string;
}

export interface UsageEventIngestParams {
  /**
   * List of events to be pushed
   */
  events: Array<EventInput>;
}

export declare namespace UsageEvents {
  export {
    type Event as Event,
    type EventInput as EventInput,
    type UsageEventIngestResponse as UsageEventIngestResponse,
    type EventsDefaultPageNumberPagination as EventsDefaultPageNumberPagination,
    type UsageEventListParams as UsageEventListParams,
    type UsageEventIngestParams as UsageEventIngestParams,
  };
}

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/subscriptions/update-subscriptions.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import DodoPayments from 'dodopayments';

export const metadata: Metadata = {
  resource: 'subscriptions',
  operation: 'write',
  tags: [],
  httpMethod: 'patch',
  httpPath: '/subscriptions/{subscription_id}',
  operationId: 'patch_subscription_handler',
};

export const tool: Tool = {
  name: 'update_subscriptions',
  description: '',
  inputSchema: {
    type: 'object',
    properties: {
      subscription_id: {
        type: 'string',
      },
      billing: {
        $ref: '#/$defs/billing_address',
      },
      cancel_at_next_billing_date: {
        type: 'boolean',
        description: 'When set, the subscription will remain active until the end of billing period',
      },
      customer_name: {
        type: 'string',
      },
      disable_on_demand: {
        type: 'object',
        title: 'Disable On Demand Request',
        properties: {
          next_billing_date: {
            type: 'string',
            format: 'date-time',
          },
        },
        required: ['next_billing_date'],
      },
      metadata: {
        type: 'object',
        additionalProperties: true,
      },
      next_billing_date: {
        type: 'string',
        format: 'date-time',
      },
      status: {
        $ref: '#/$defs/subscription_status',
      },
      tax_id: {
        type: 'string',
      },
    },
    required: ['subscription_id'],
    $defs: {
      billing_address: {
        type: 'object',
        properties: {
          city: {
            type: 'string',
            description: 'City name',
          },
          country: {
            $ref: '#/$defs/country_code',
          },
          state: {
            type: 'string',
            description: 'State or province name',
          },
          street: {
            type: 'string',
            description: 'Street address including house number and unit/apartment if applicable',
          },
          zipcode: {
            type: 'string',
            description: 'Postal code or ZIP code',
          },
        },
        required: ['city', 'country', 'state', 'street', 'zipcode'],
      },
      country_code: {
        type: 'string',
        description: 'ISO country code alpha2 variant',
        enum: [
          'AF',
          'AX',
          'AL',
          'DZ',
          'AS',
          'AD',
          'AO',
          'AI',
          'AQ',
          'AG',
          'AR',
          'AM',
          'AW',
          'AU',
          'AT',
          'AZ',
          'BS',
          'BH',
          'BD',
          'BB',
          'BY',
          'BE',
          'BZ',
          'BJ',
          'BM',
          'BT',
          'BO',
          'BQ',
          'BA',
          'BW',
          'BV',
          'BR',
          'IO',
          'BN',
          'BG',
          'BF',
          'BI',
          'KH',
          'CM',
          'CA',
          'CV',
          'KY',
          'CF',
          'TD',
          'CL',
          'CN',
          'CX',
          'CC',
          'CO',
          'KM',
          'CG',
          'CD',
          'CK',
          'CR',
          'CI',
          'HR',
          'CU',
          'CW',
          'CY',
          'CZ',
          'DK',
          'DJ',
          'DM',
          'DO',
          'EC',
          'EG',
          'SV',
          'GQ',
          'ER',
          'EE',
          'ET',
          'FK',
          'FO',
          'FJ',
          'FI',
          'FR',
          'GF',
          'PF',
          'TF',
          'GA',
          'GM',
          'GE',
          'DE',
          'GH',
          'GI',
          'GR',
          'GL',
          'GD',
          'GP',
          'GU',
          'GT',
          'GG',
          'GN',
          'GW',
          'GY',
          'HT',
          'HM',
          'VA',
          'HN',
          'HK',
          'HU',
          'IS',
          'IN',
          'ID',
          'IR',
          'IQ',
          'IE',
          'IM',
          'IL',
          'IT',
          'JM',
          'JP',
          'JE',
          'JO',
          'KZ',
          'KE',
          'KI',
          'KP',
          'KR',
          'KW',
          'KG',
          'LA',
          'LV',
          'LB',
          'LS',
          'LR',
          'LY',
          'LI',
          'LT',
          'LU',
          'MO',
          'MK',
          'MG',
          'MW',
          'MY',
          'MV',
          'ML',
          'MT',
          'MH',
          'MQ',
          'MR',
          'MU',
          'YT',
          'MX',
          'FM',
          'MD',
          'MC',
          'MN',
          'ME',
          'MS',
          'MA',
          'MZ',
          'MM',
          'NA',
          'NR',
          'NP',
          'NL',
          'NC',
          'NZ',
          'NI',
          'NE',
          'NG',
          'NU',
          'NF',
          'MP',
          'NO',
          'OM',
          'PK',
          'PW',
          'PS',
          'PA',
          'PG',
          'PY',
          'PE',
          'PH',
          'PN',
          'PL',
          'PT',
          'PR',
          'QA',
          'RE',
          'RO',
          'RU',
          'RW',
          'BL',
          'SH',
          'KN',
          'LC',
          'MF',
          'PM',
          'VC',
          'WS',
          'SM',
          'ST',
          'SA',
          'SN',
          'RS',
          'SC',
          'SL',
          'SG',
          'SX',
          'SK',
          'SI',
          'SB',
          'SO',
          'ZA',
          'GS',
          'SS',
          'ES',
          'LK',
          'SD',
          'SR',
          'SJ',
          'SZ',
          'SE',
          'CH',
          'SY',
          'TW',
          'TJ',
          'TZ',
          'TH',
          'TL',
          'TG',
          'TK',
          'TO',
          'TT',
          'TN',
          'TR',
          'TM',
          'TC',
          'TV',
          'UG',
          'UA',
          'AE',
          'GB',
          'UM',
          'US',
          'UY',
          'UZ',
          'VU',
          'VE',
          'VN',
          'VG',
          'VI',
          'WF',
          'EH',
          'YE',
          'ZM',
          'ZW',
        ],
      },
      subscription_status: {
        type: 'string',
        enum: ['pending', 'active', 'on_hold', 'cancelled', 'failed', 'expired'],
      },
    },
  },
  annotations: {},
};

export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
  const { subscription_id, ...body } = args as any;
  return asTextContentResult(await client.subscriptions.update(subscription_id, body));
};

export default { metadata, tool, handler };

```

--------------------------------------------------------------------------------
/tests/api-resources/subscriptions.test.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  bearerToken: 'My Bearer Token',
  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
});

describe('resource subscriptions', () => {
  test('create: only required params', async () => {
    const responsePromise = client.subscriptions.create({
      billing: { city: 'city', country: 'AF', state: 'state', street: 'street', zipcode: 'zipcode' },
      customer: { customer_id: 'customer_id' },
      product_id: 'product_id',
      quantity: 0,
    });
    const rawResponse = await responsePromise.asResponse();
    expect(rawResponse).toBeInstanceOf(Response);
    const response = await responsePromise;
    expect(response).not.toBeInstanceOf(Response);
    const dataAndResponse = await responsePromise.withResponse();
    expect(dataAndResponse.data).toBe(response);
    expect(dataAndResponse.response).toBe(rawResponse);
  });

  test('create: required and optional params', async () => {
    const response = await client.subscriptions.create({
      billing: { city: 'city', country: 'AF', state: 'state', street: 'street', zipcode: 'zipcode' },
      customer: { customer_id: 'customer_id' },
      product_id: 'product_id',
      quantity: 0,
      addons: [{ addon_id: 'addon_id', quantity: 0 }],
      allowed_payment_method_types: ['credit'],
      billing_currency: 'AED',
      discount_code: 'discount_code',
      force_3ds: true,
      metadata: { foo: 'string' },
      on_demand: {
        mandate_only: true,
        adaptive_currency_fees_inclusive: true,
        product_currency: 'AED',
        product_description: 'product_description',
        product_price: 0,
      },
      payment_link: true,
      return_url: 'return_url',
      show_saved_payment_methods: true,
      tax_id: 'tax_id',
      trial_period_days: 0,
    });
  });

  test('retrieve', async () => {
    const responsePromise = client.subscriptions.retrieve('subscription_id');
    const rawResponse = await responsePromise.asResponse();
    expect(rawResponse).toBeInstanceOf(Response);
    const response = await responsePromise;
    expect(response).not.toBeInstanceOf(Response);
    const dataAndResponse = await responsePromise.withResponse();
    expect(dataAndResponse.data).toBe(response);
    expect(dataAndResponse.response).toBe(rawResponse);
  });

  test('update', async () => {
    const responsePromise = client.subscriptions.update('subscription_id', {});
    const rawResponse = await responsePromise.asResponse();
    expect(rawResponse).toBeInstanceOf(Response);
    const response = await responsePromise;
    expect(response).not.toBeInstanceOf(Response);
    const dataAndResponse = await responsePromise.withResponse();
    expect(dataAndResponse.data).toBe(response);
    expect(dataAndResponse.response).toBe(rawResponse);
  });

  test('list', async () => {
    const responsePromise = client.subscriptions.list();
    const rawResponse = await responsePromise.asResponse();
    expect(rawResponse).toBeInstanceOf(Response);
    const response = await responsePromise;
    expect(response).not.toBeInstanceOf(Response);
    const dataAndResponse = await responsePromise.withResponse();
    expect(dataAndResponse.data).toBe(response);
    expect(dataAndResponse.response).toBe(rawResponse);
  });

  test('list: request options and params are passed correctly', async () => {
    // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
    await expect(
      client.subscriptions.list(
        {
          brand_id: 'brand_id',
          created_at_gte: '2019-12-27T18:11:19.117Z',
          created_at_lte: '2019-12-27T18:11:19.117Z',
          customer_id: 'customer_id',
          page_number: 0,
          page_size: 0,
          status: 'pending',
        },
        { path: '/_stainless_unknown_path' },
      ),
    ).rejects.toThrow(DodoPayments.NotFoundError);
  });

  test('changePlan: only required params', async () => {
    const responsePromise = client.subscriptions.changePlan('subscription_id', {
      product_id: 'product_id',
      proration_billing_mode: 'prorated_immediately',
      quantity: 0,
    });
    const rawResponse = await responsePromise.asResponse();
    expect(rawResponse).toBeInstanceOf(Response);
    const response = await responsePromise;
    expect(response).not.toBeInstanceOf(Response);
    const dataAndResponse = await responsePromise.withResponse();
    expect(dataAndResponse.data).toBe(response);
    expect(dataAndResponse.response).toBe(rawResponse);
  });

  test('changePlan: required and optional params', async () => {
    const response = await client.subscriptions.changePlan('subscription_id', {
      product_id: 'product_id',
      proration_billing_mode: 'prorated_immediately',
      quantity: 0,
      addons: [{ addon_id: 'addon_id', quantity: 0 }],
    });
  });

  test('charge: only required params', async () => {
    const responsePromise = client.subscriptions.charge('subscription_id', { product_price: 0 });
    const rawResponse = await responsePromise.asResponse();
    expect(rawResponse).toBeInstanceOf(Response);
    const response = await responsePromise;
    expect(response).not.toBeInstanceOf(Response);
    const dataAndResponse = await responsePromise.withResponse();
    expect(dataAndResponse.data).toBe(response);
    expect(dataAndResponse.response).toBe(rawResponse);
  });

  test('charge: required and optional params', async () => {
    const response = await client.subscriptions.charge('subscription_id', {
      product_price: 0,
      adaptive_currency_fees_inclusive: true,
      customer_balance_config: { allow_customer_credits_purchase: true, allow_customer_credits_usage: true },
      metadata: { foo: 'string' },
      product_currency: 'AED',
      product_description: 'product_description',
    });
  });

  test('retrieveUsageHistory', async () => {
    const responsePromise = client.subscriptions.retrieveUsageHistory('subscription_id');
    const rawResponse = await responsePromise.asResponse();
    expect(rawResponse).toBeInstanceOf(Response);
    const response = await responsePromise;
    expect(response).not.toBeInstanceOf(Response);
    const dataAndResponse = await responsePromise.withResponse();
    expect(dataAndResponse.data).toBe(response);
    expect(dataAndResponse.response).toBe(rawResponse);
  });

  test('retrieveUsageHistory: request options and params are passed correctly', async () => {
    // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
    await expect(
      client.subscriptions.retrieveUsageHistory(
        'subscription_id',
        {
          end_date: '2019-12-27T18:11:19.117Z',
          meter_id: 'meter_id',
          page_number: 0,
          page_size: 0,
          start_date: '2019-12-27T18:11:19.117Z',
        },
        { path: '/_stainless_unknown_path' },
      ),
    ).rejects.toThrow(DodoPayments.NotFoundError);
  });
});

```

--------------------------------------------------------------------------------
/src/resources/meters.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { APIResource } from '../core/resource';
import { APIPromise } from '../core/api-promise';
import {
  DefaultPageNumberPagination,
  type DefaultPageNumberPaginationParams,
  PagePromise,
} from '../core/pagination';
import { buildHeaders } from '../internal/headers';
import { RequestOptions } from '../internal/request-options';
import { path } from '../internal/utils/path';

export class Meters extends APIResource {
  create(body: MeterCreateParams, options?: RequestOptions): APIPromise<Meter> {
    return this._client.post('/meters', { body, ...options });
  }

  retrieve(id: string, options?: RequestOptions): APIPromise<Meter> {
    return this._client.get(path`/meters/${id}`, options);
  }

  list(
    query: MeterListParams | null | undefined = {},
    options?: RequestOptions,
  ): PagePromise<MetersDefaultPageNumberPagination, Meter> {
    return this._client.getAPIList('/meters', DefaultPageNumberPagination<Meter>, { query, ...options });
  }

  archive(id: string, options?: RequestOptions): APIPromise<void> {
    return this._client.delete(path`/meters/${id}`, {
      ...options,
      headers: buildHeaders([{ Accept: '*/*' }, options?.headers]),
    });
  }

  unarchive(id: string, options?: RequestOptions): APIPromise<void> {
    return this._client.post(path`/meters/${id}/unarchive`, {
      ...options,
      headers: buildHeaders([{ Accept: '*/*' }, options?.headers]),
    });
  }
}

export type MetersDefaultPageNumberPagination = DefaultPageNumberPagination<Meter>;

export interface Meter {
  id: string;

  aggregation: MeterAggregation;

  business_id: string;

  created_at: string;

  event_name: string;

  measurement_unit: string;

  name: string;

  updated_at: string;

  description?: string | null;

  /**
   * A filter structure that combines multiple conditions with logical conjunctions
   * (AND/OR).
   *
   * Supports up to 3 levels of nesting to create complex filter expressions. Each
   * filter has a conjunction (and/or) and clauses that can be either direct
   * conditions or nested filters.
   */
  filter?: MeterFilter | null;
}

export interface MeterAggregation {
  /**
   * Aggregation type for the meter
   */
  type: 'count' | 'sum' | 'max' | 'last';

  /**
   * Required when type is not COUNT
   */
  key?: string | null;
}

/**
 * A filter structure that combines multiple conditions with logical conjunctions
 * (AND/OR).
 *
 * Supports up to 3 levels of nesting to create complex filter expressions. Each
 * filter has a conjunction (and/or) and clauses that can be either direct
 * conditions or nested filters.
 */
export interface MeterFilter {
  /**
   * Filter clauses - can be direct conditions or nested filters (up to 3 levels
   * deep)
   */
  clauses: Array<MeterFilter.DirectFilterCondition> | Array<MeterFilter.NestedMeterFilter>;

  /**
   * Logical conjunction to apply between clauses (and/or)
   */
  conjunction: 'and' | 'or';
}

export namespace MeterFilter {
  /**
   * Filter condition with key, operator, and value
   */
  export interface DirectFilterCondition {
    /**
     * Filter key to apply
     */
    key: string;

    operator:
      | 'equals'
      | 'not_equals'
      | 'greater_than'
      | 'greater_than_or_equals'
      | 'less_than'
      | 'less_than_or_equals'
      | 'contains'
      | 'does_not_contain';

    /**
     * Filter value - can be string, number, or boolean
     */
    value: string | number | boolean;
  }

  /**
   * Level 1 nested filter - can contain Level 2 filters
   */
  export interface NestedMeterFilter {
    /**
     * Level 1: Can be conditions or nested filters (2 more levels allowed)
     */
    clauses: Array<NestedMeterFilter.Level1FilterCondition> | Array<NestedMeterFilter.Level1NestedFilter>;

    conjunction: 'and' | 'or';
  }

  export namespace NestedMeterFilter {
    /**
     * Filter condition with key, operator, and value
     */
    export interface Level1FilterCondition {
      /**
       * Filter key to apply
       */
      key: string;

      operator:
        | 'equals'
        | 'not_equals'
        | 'greater_than'
        | 'greater_than_or_equals'
        | 'less_than'
        | 'less_than_or_equals'
        | 'contains'
        | 'does_not_contain';

      /**
       * Filter value - can be string, number, or boolean
       */
      value: string | number | boolean;
    }

    /**
     * Level 2 nested filter
     */
    export interface Level1NestedFilter {
      /**
       * Level 2: Can be conditions or nested filters (1 more level allowed)
       */
      clauses: Array<Level1NestedFilter.Level2FilterCondition> | Array<Level1NestedFilter.Level2NestedFilter>;

      conjunction: 'and' | 'or';
    }

    export namespace Level1NestedFilter {
      /**
       * Filter condition with key, operator, and value
       */
      export interface Level2FilterCondition {
        /**
         * Filter key to apply
         */
        key: string;

        operator:
          | 'equals'
          | 'not_equals'
          | 'greater_than'
          | 'greater_than_or_equals'
          | 'less_than'
          | 'less_than_or_equals'
          | 'contains'
          | 'does_not_contain';

        /**
         * Filter value - can be string, number, or boolean
         */
        value: string | number | boolean;
      }

      /**
       * Level 3 nested filter (final nesting level)
       */
      export interface Level2NestedFilter {
        /**
         * Level 3: Filter conditions only (max depth reached)
         */
        clauses: Array<Level2NestedFilter.Clause>;

        conjunction: 'and' | 'or';
      }

      export namespace Level2NestedFilter {
        /**
         * Filter condition with key, operator, and value
         */
        export interface Clause {
          /**
           * Filter key to apply
           */
          key: string;

          operator:
            | 'equals'
            | 'not_equals'
            | 'greater_than'
            | 'greater_than_or_equals'
            | 'less_than'
            | 'less_than_or_equals'
            | 'contains'
            | 'does_not_contain';

          /**
           * Filter value - can be string, number, or boolean
           */
          value: string | number | boolean;
        }
      }
    }
  }
}

export interface MeterCreateParams {
  /**
   * Aggregation configuration for the meter
   */
  aggregation: MeterAggregation;

  /**
   * Event name to track
   */
  event_name: string;

  /**
   * measurement unit
   */
  measurement_unit: string;

  /**
   * Name of the meter
   */
  name: string;

  /**
   * Optional description of the meter
   */
  description?: string | null;

  /**
   * Optional filter to apply to the meter
   */
  filter?: MeterFilter | null;
}

export interface MeterListParams extends DefaultPageNumberPaginationParams {
  /**
   * List archived meters
   */
  archived?: boolean;
}

export declare namespace Meters {
  export {
    type Meter as Meter,
    type MeterAggregation as MeterAggregation,
    type MeterFilter as MeterFilter,
    type MetersDefaultPageNumberPagination as MetersDefaultPageNumberPagination,
    type MeterCreateParams as MeterCreateParams,
    type MeterListParams as MeterListParams,
  };
}

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/refunds/create-refunds.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { maybeFilter } from 'dodopayments-mcp/filtering';
import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import DodoPayments from 'dodopayments';

export const metadata: Metadata = {
  resource: 'refunds',
  operation: 'write',
  tags: [],
  httpMethod: 'post',
  httpPath: '/refunds',
  operationId: 'create_refund_handler',
};

export const tool: Tool = {
  name: 'create_refunds',
  description:
    "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n\n\n# Response Schema\n```json\n{\n  $ref: '#/$defs/refund',\n  $defs: {\n    refund: {\n      type: 'object',\n      properties: {\n        business_id: {\n          type: 'string',\n          description: 'The unique identifier of the business issuing the refund.'\n        },\n        created_at: {\n          type: 'string',\n          description: 'The timestamp of when the refund was created in UTC.',\n          format: 'date-time'\n        },\n        customer: {\n          $ref: '#/$defs/customer_limited_details'\n        },\n        is_partial: {\n          type: 'boolean',\n          description: 'If true the refund is a partial refund'\n        },\n        payment_id: {\n          type: 'string',\n          description: 'The unique identifier of the payment associated with the refund.'\n        },\n        refund_id: {\n          type: 'string',\n          description: 'The unique identifier of the refund.'\n        },\n        status: {\n          $ref: '#/$defs/refund_status'\n        },\n        amount: {\n          type: 'integer',\n          description: 'The refunded amount.'\n        },\n        currency: {\n          $ref: '#/$defs/currency'\n        },\n        reason: {\n          type: 'string',\n          description: 'The reason provided for the refund, if any. Optional.'\n        }\n      },\n      required: [        'business_id',\n        'created_at',\n        'customer',\n        'is_partial',\n        'payment_id',\n        'refund_id',\n        'status'\n      ]\n    },\n    customer_limited_details: {\n      type: 'object',\n      properties: {\n        customer_id: {\n          type: 'string',\n          description: 'Unique identifier for the customer'\n        },\n        email: {\n          type: 'string',\n          description: 'Email address of the customer'\n        },\n        name: {\n          type: 'string',\n          description: 'Full name of the customer'\n        },\n        phone_number: {\n          type: 'string',\n          description: 'Phone number of the customer'\n        }\n      },\n      required: [        'customer_id',\n        'email',\n        'name'\n      ]\n    },\n    refund_status: {\n      type: 'string',\n      enum: [        'succeeded',\n        'failed',\n        'pending',\n        'review'\n      ]\n    },\n    currency: {\n      type: 'string',\n      enum: [        'AED',\n        'ALL',\n        'AMD',\n        'ANG',\n        'AOA',\n        'ARS',\n        'AUD',\n        'AWG',\n        'AZN',\n        'BAM',\n        'BBD',\n        'BDT',\n        'BGN',\n        'BHD',\n        'BIF',\n        'BMD',\n        'BND',\n        'BOB',\n        'BRL',\n        'BSD',\n        'BWP',\n        'BYN',\n        'BZD',\n        'CAD',\n        'CHF',\n        'CLP',\n        'CNY',\n        'COP',\n        'CRC',\n        'CUP',\n        'CVE',\n        'CZK',\n        'DJF',\n        'DKK',\n        'DOP',\n        'DZD',\n        'EGP',\n        'ETB',\n        'EUR',\n        'FJD',\n        'FKP',\n        'GBP',\n        'GEL',\n        'GHS',\n        'GIP',\n        'GMD',\n        'GNF',\n        'GTQ',\n        'GYD',\n        'HKD',\n        'HNL',\n        'HRK',\n        'HTG',\n        'HUF',\n        'IDR',\n        'ILS',\n        'INR',\n        'IQD',\n        'JMD',\n        'JOD',\n        'JPY',\n        'KES',\n        'KGS',\n        'KHR',\n        'KMF',\n        'KRW',\n        'KWD',\n        'KYD',\n        'KZT',\n        'LAK',\n        'LBP',\n        'LKR',\n        'LRD',\n        'LSL',\n        'LYD',\n        'MAD',\n        'MDL',\n        'MGA',\n        'MKD',\n        'MMK',\n        'MNT',\n        'MOP',\n        'MRU',\n        'MUR',\n        'MVR',\n        'MWK',\n        'MXN',\n        'MYR',\n        'MZN',\n        'NAD',\n        'NGN',\n        'NIO',\n        'NOK',\n        'NPR',\n        'NZD',\n        'OMR',\n        'PAB',\n        'PEN',\n        'PGK',\n        'PHP',\n        'PKR',\n        'PLN',\n        'PYG',\n        'QAR',\n        'RON',\n        'RSD',\n        'RUB',\n        'RWF',\n        'SAR',\n        'SBD',\n        'SCR',\n        'SEK',\n        'SGD',\n        'SHP',\n        'SLE',\n        'SLL',\n        'SOS',\n        'SRD',\n        'SSP',\n        'STN',\n        'SVC',\n        'SZL',\n        'THB',\n        'TND',\n        'TOP',\n        'TRY',\n        'TTD',\n        'TWD',\n        'TZS',\n        'UAH',\n        'UGX',\n        'USD',\n        'UYU',\n        'UZS',\n        'VES',\n        'VND',\n        'VUV',\n        'WST',\n        'XAF',\n        'XCD',\n        'XOF',\n        'XPF',\n        'YER',\n        'ZAR',\n        'ZMW'\n      ]\n    }\n  }\n}\n```",
  inputSchema: {
    type: 'object',
    properties: {
      payment_id: {
        type: 'string',
        description: 'The unique identifier of the payment to be refunded.',
      },
      items: {
        type: 'array',
        description: 'Partially Refund an Individual Item',
        items: {
          type: 'object',
          properties: {
            item_id: {
              type: 'string',
              description: 'The id of the item (i.e. `product_id` or `addon_id`)',
            },
            amount: {
              type: 'integer',
              description: 'The amount to refund. if None the whole item is refunded',
            },
            tax_inclusive: {
              type: 'boolean',
              description: 'Specify if tax is inclusive of the refund. Default true.',
            },
          },
          required: ['item_id'],
        },
      },
      reason: {
        type: 'string',
        description: 'The reason for the refund, if any. Maximum length is 3000 characters. Optional.',
      },
      jq_filter: {
        type: 'string',
        title: 'jq Filter',
        description:
          'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
      },
    },
    required: ['payment_id'],
  },
  annotations: {},
};

export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
  const { jq_filter, ...body } = args as any;
  return asTextContentResult(await maybeFilter(jq_filter, await client.refunds.create(body)));
};

export default { metadata, tool, handler };

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/payouts/list-payouts.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { maybeFilter } from 'dodopayments-mcp/filtering';
import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import DodoPayments from 'dodopayments';

export const metadata: Metadata = {
  resource: 'payouts',
  operation: 'read',
  tags: [],
  httpMethod: 'get',
  httpPath: '/payouts',
  operationId: 'list_payouts',
};

export const tool: Tool = {
  name: 'list_payouts',
  description:
    "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n\n\n# Response Schema\n```json\n{\n  type: 'object',\n  properties: {\n    items: {\n      type: 'array',\n      items: {\n        $ref: '#/$defs/payout_list_response'\n      }\n    }\n  },\n  required: [    'items'\n  ],\n  $defs: {\n    payout_list_response: {\n      type: 'object',\n      properties: {\n        amount: {\n          type: 'integer',\n          description: 'The total amount of the payout.'\n        },\n        business_id: {\n          type: 'string',\n          description: 'The unique identifier of the business associated with the payout.'\n        },\n        chargebacks: {\n          type: 'integer',\n          description: 'The total value of chargebacks associated with the payout.'\n        },\n        created_at: {\n          type: 'string',\n          description: 'The timestamp when the payout was created, in UTC.',\n          format: 'date-time'\n        },\n        currency: {\n          $ref: '#/$defs/currency'\n        },\n        fee: {\n          type: 'integer',\n          description: 'The fee charged for processing the payout.'\n        },\n        payment_method: {\n          type: 'string',\n          description: 'The payment method used for the payout (e.g., bank transfer, card, etc.).'\n        },\n        payout_id: {\n          type: 'string',\n          description: 'The unique identifier of the payout.'\n        },\n        refunds: {\n          type: 'integer',\n          description: 'The total value of refunds associated with the payout.'\n        },\n        status: {\n          type: 'string',\n          description: 'The current status of the payout.',\n          enum: [            'not_initiated',\n            'in_progress',\n            'on_hold',\n            'failed',\n            'success'\n          ]\n        },\n        tax: {\n          type: 'integer',\n          description: 'The tax applied to the payout.'\n        },\n        updated_at: {\n          type: 'string',\n          description: 'The timestamp when the payout was last updated, in UTC.',\n          format: 'date-time'\n        },\n        name: {\n          type: 'string',\n          description: 'The name of the payout recipient or purpose.'\n        },\n        payout_document_url: {\n          type: 'string',\n          description: 'The URL of the document associated with the payout.'\n        },\n        remarks: {\n          type: 'string',\n          description: 'Any additional remarks or notes associated with the payout.'\n        }\n      },\n      required: [        'amount',\n        'business_id',\n        'chargebacks',\n        'created_at',\n        'currency',\n        'fee',\n        'payment_method',\n        'payout_id',\n        'refunds',\n        'status',\n        'tax',\n        'updated_at'\n      ]\n    },\n    currency: {\n      type: 'string',\n      enum: [        'AED',\n        'ALL',\n        'AMD',\n        'ANG',\n        'AOA',\n        'ARS',\n        'AUD',\n        'AWG',\n        'AZN',\n        'BAM',\n        'BBD',\n        'BDT',\n        'BGN',\n        'BHD',\n        'BIF',\n        'BMD',\n        'BND',\n        'BOB',\n        'BRL',\n        'BSD',\n        'BWP',\n        'BYN',\n        'BZD',\n        'CAD',\n        'CHF',\n        'CLP',\n        'CNY',\n        'COP',\n        'CRC',\n        'CUP',\n        'CVE',\n        'CZK',\n        'DJF',\n        'DKK',\n        'DOP',\n        'DZD',\n        'EGP',\n        'ETB',\n        'EUR',\n        'FJD',\n        'FKP',\n        'GBP',\n        'GEL',\n        'GHS',\n        'GIP',\n        'GMD',\n        'GNF',\n        'GTQ',\n        'GYD',\n        'HKD',\n        'HNL',\n        'HRK',\n        'HTG',\n        'HUF',\n        'IDR',\n        'ILS',\n        'INR',\n        'IQD',\n        'JMD',\n        'JOD',\n        'JPY',\n        'KES',\n        'KGS',\n        'KHR',\n        'KMF',\n        'KRW',\n        'KWD',\n        'KYD',\n        'KZT',\n        'LAK',\n        'LBP',\n        'LKR',\n        'LRD',\n        'LSL',\n        'LYD',\n        'MAD',\n        'MDL',\n        'MGA',\n        'MKD',\n        'MMK',\n        'MNT',\n        'MOP',\n        'MRU',\n        'MUR',\n        'MVR',\n        'MWK',\n        'MXN',\n        'MYR',\n        'MZN',\n        'NAD',\n        'NGN',\n        'NIO',\n        'NOK',\n        'NPR',\n        'NZD',\n        'OMR',\n        'PAB',\n        'PEN',\n        'PGK',\n        'PHP',\n        'PKR',\n        'PLN',\n        'PYG',\n        'QAR',\n        'RON',\n        'RSD',\n        'RUB',\n        'RWF',\n        'SAR',\n        'SBD',\n        'SCR',\n        'SEK',\n        'SGD',\n        'SHP',\n        'SLE',\n        'SLL',\n        'SOS',\n        'SRD',\n        'SSP',\n        'STN',\n        'SVC',\n        'SZL',\n        'THB',\n        'TND',\n        'TOP',\n        'TRY',\n        'TTD',\n        'TWD',\n        'TZS',\n        'UAH',\n        'UGX',\n        'USD',\n        'UYU',\n        'UZS',\n        'VES',\n        'VND',\n        'VUV',\n        'WST',\n        'XAF',\n        'XCD',\n        'XOF',\n        'XPF',\n        'YER',\n        'ZAR',\n        'ZMW'\n      ]\n    }\n  }\n}\n```",
  inputSchema: {
    type: 'object',
    properties: {
      created_at_gte: {
        type: 'string',
        description: 'Get payouts created after this time (inclusive)',
        format: 'date-time',
      },
      created_at_lte: {
        type: 'string',
        description: 'Get payouts created before this time (inclusive)',
        format: 'date-time',
      },
      page_number: {
        type: 'integer',
        description: 'Page number default is 0',
      },
      page_size: {
        type: 'integer',
        description: 'Page size default is 10 max is 100',
      },
      jq_filter: {
        type: 'string',
        title: 'jq Filter',
        description:
          'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
      },
    },
    required: [],
  },
  annotations: {
    readOnlyHint: true,
  },
};

export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
  const { jq_filter, ...body } = args as any;
  const response = await client.payouts.list(body).asResponse();
  return asTextContentResult(await maybeFilter(jq_filter, await response.json()));
};

export default { metadata, tool, handler };

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/payments/list-payments.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { maybeFilter } from 'dodopayments-mcp/filtering';
import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import DodoPayments from 'dodopayments';

export const metadata: Metadata = {
  resource: 'payments',
  operation: 'read',
  tags: [],
  httpMethod: 'get',
  httpPath: '/payments',
  operationId: 'list_payments_handler',
};

export const tool: Tool = {
  name: 'list_payments',
  description:
    "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n\n\n# Response Schema\n```json\n{\n  type: 'object',\n  properties: {\n    items: {\n      type: 'array',\n      items: {\n        $ref: '#/$defs/payment_list_response'\n      }\n    }\n  },\n  required: [    'items'\n  ],\n  $defs: {\n    payment_list_response: {\n      type: 'object',\n      properties: {\n        brand_id: {\n          type: 'string'\n        },\n        created_at: {\n          type: 'string',\n          format: 'date-time'\n        },\n        currency: {\n          $ref: '#/$defs/currency'\n        },\n        customer: {\n          $ref: '#/$defs/customer_limited_details'\n        },\n        digital_products_delivered: {\n          type: 'boolean'\n        },\n        metadata: {\n          type: 'object',\n          additionalProperties: true\n        },\n        payment_id: {\n          type: 'string'\n        },\n        total_amount: {\n          type: 'integer'\n        },\n        payment_method: {\n          type: 'string'\n        },\n        payment_method_type: {\n          type: 'string'\n        },\n        status: {\n          $ref: '#/$defs/intent_status'\n        },\n        subscription_id: {\n          type: 'string'\n        }\n      },\n      required: [        'brand_id',\n        'created_at',\n        'currency',\n        'customer',\n        'digital_products_delivered',\n        'metadata',\n        'payment_id',\n        'total_amount'\n      ]\n    },\n    currency: {\n      type: 'string',\n      enum: [        'AED',\n        'ALL',\n        'AMD',\n        'ANG',\n        'AOA',\n        'ARS',\n        'AUD',\n        'AWG',\n        'AZN',\n        'BAM',\n        'BBD',\n        'BDT',\n        'BGN',\n        'BHD',\n        'BIF',\n        'BMD',\n        'BND',\n        'BOB',\n        'BRL',\n        'BSD',\n        'BWP',\n        'BYN',\n        'BZD',\n        'CAD',\n        'CHF',\n        'CLP',\n        'CNY',\n        'COP',\n        'CRC',\n        'CUP',\n        'CVE',\n        'CZK',\n        'DJF',\n        'DKK',\n        'DOP',\n        'DZD',\n        'EGP',\n        'ETB',\n        'EUR',\n        'FJD',\n        'FKP',\n        'GBP',\n        'GEL',\n        'GHS',\n        'GIP',\n        'GMD',\n        'GNF',\n        'GTQ',\n        'GYD',\n        'HKD',\n        'HNL',\n        'HRK',\n        'HTG',\n        'HUF',\n        'IDR',\n        'ILS',\n        'INR',\n        'IQD',\n        'JMD',\n        'JOD',\n        'JPY',\n        'KES',\n        'KGS',\n        'KHR',\n        'KMF',\n        'KRW',\n        'KWD',\n        'KYD',\n        'KZT',\n        'LAK',\n        'LBP',\n        'LKR',\n        'LRD',\n        'LSL',\n        'LYD',\n        'MAD',\n        'MDL',\n        'MGA',\n        'MKD',\n        'MMK',\n        'MNT',\n        'MOP',\n        'MRU',\n        'MUR',\n        'MVR',\n        'MWK',\n        'MXN',\n        'MYR',\n        'MZN',\n        'NAD',\n        'NGN',\n        'NIO',\n        'NOK',\n        'NPR',\n        'NZD',\n        'OMR',\n        'PAB',\n        'PEN',\n        'PGK',\n        'PHP',\n        'PKR',\n        'PLN',\n        'PYG',\n        'QAR',\n        'RON',\n        'RSD',\n        'RUB',\n        'RWF',\n        'SAR',\n        'SBD',\n        'SCR',\n        'SEK',\n        'SGD',\n        'SHP',\n        'SLE',\n        'SLL',\n        'SOS',\n        'SRD',\n        'SSP',\n        'STN',\n        'SVC',\n        'SZL',\n        'THB',\n        'TND',\n        'TOP',\n        'TRY',\n        'TTD',\n        'TWD',\n        'TZS',\n        'UAH',\n        'UGX',\n        'USD',\n        'UYU',\n        'UZS',\n        'VES',\n        'VND',\n        'VUV',\n        'WST',\n        'XAF',\n        'XCD',\n        'XOF',\n        'XPF',\n        'YER',\n        'ZAR',\n        'ZMW'\n      ]\n    },\n    customer_limited_details: {\n      type: 'object',\n      properties: {\n        customer_id: {\n          type: 'string',\n          description: 'Unique identifier for the customer'\n        },\n        email: {\n          type: 'string',\n          description: 'Email address of the customer'\n        },\n        name: {\n          type: 'string',\n          description: 'Full name of the customer'\n        },\n        phone_number: {\n          type: 'string',\n          description: 'Phone number of the customer'\n        }\n      },\n      required: [        'customer_id',\n        'email',\n        'name'\n      ]\n    },\n    intent_status: {\n      type: 'string',\n      enum: [        'succeeded',\n        'failed',\n        'cancelled',\n        'processing',\n        'requires_customer_action',\n        'requires_merchant_action',\n        'requires_payment_method',\n        'requires_confirmation',\n        'requires_capture',\n        'partially_captured',\n        'partially_captured_and_capturable'\n      ]\n    }\n  }\n}\n```",
  inputSchema: {
    type: 'object',
    properties: {
      brand_id: {
        type: 'string',
        description: 'filter by Brand id',
      },
      created_at_gte: {
        type: 'string',
        description: 'Get events after this created time',
        format: 'date-time',
      },
      created_at_lte: {
        type: 'string',
        description: 'Get events created before this time',
        format: 'date-time',
      },
      customer_id: {
        type: 'string',
        description: 'Filter by customer id',
      },
      page_number: {
        type: 'integer',
        description: 'Page number default is 0',
      },
      page_size: {
        type: 'integer',
        description: 'Page size default is 10 max is 100',
      },
      status: {
        type: 'string',
        description: 'Filter by status',
        enum: [
          'succeeded',
          'failed',
          'cancelled',
          'processing',
          'requires_customer_action',
          'requires_merchant_action',
          'requires_payment_method',
          'requires_confirmation',
          'requires_capture',
          'partially_captured',
          'partially_captured_and_capturable',
        ],
      },
      subscription_id: {
        type: 'string',
        description: 'Filter by subscription id',
      },
      jq_filter: {
        type: 'string',
        title: 'jq Filter',
        description:
          'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
      },
    },
    required: [],
  },
  annotations: {
    readOnlyHint: true,
  },
};

export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
  const { jq_filter, ...body } = args as any;
  const response = await client.payments.list(body).asResponse();
  return asTextContentResult(await maybeFilter(jq_filter, await response.json()));
};

export default { metadata, tool, handler };

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/customers/wallets/ledger-entries/create-wallets-customers-ledger-entries.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { maybeFilter } from 'dodopayments-mcp/filtering';
import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import DodoPayments from 'dodopayments';

export const metadata: Metadata = {
  resource: 'customers.wallets.ledger_entries',
  operation: 'write',
  tags: [],
  httpMethod: 'post',
  httpPath: '/customers/{customer_id}/wallets/ledger-entries',
  operationId: 'create_customer_ledger_entry',
};

export const tool: Tool = {
  name: 'create_wallets_customers_ledger_entries',
  description:
    "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n\n\n# Response Schema\n```json\n{\n  $ref: '#/$defs/customer_wallet',\n  $defs: {\n    customer_wallet: {\n      type: 'object',\n      properties: {\n        balance: {\n          type: 'integer'\n        },\n        created_at: {\n          type: 'string',\n          format: 'date-time'\n        },\n        currency: {\n          $ref: '#/$defs/currency'\n        },\n        customer_id: {\n          type: 'string'\n        },\n        updated_at: {\n          type: 'string',\n          format: 'date-time'\n        }\n      },\n      required: [        'balance',\n        'created_at',\n        'currency',\n        'customer_id',\n        'updated_at'\n      ]\n    },\n    currency: {\n      type: 'string',\n      enum: [        'AED',\n        'ALL',\n        'AMD',\n        'ANG',\n        'AOA',\n        'ARS',\n        'AUD',\n        'AWG',\n        'AZN',\n        'BAM',\n        'BBD',\n        'BDT',\n        'BGN',\n        'BHD',\n        'BIF',\n        'BMD',\n        'BND',\n        'BOB',\n        'BRL',\n        'BSD',\n        'BWP',\n        'BYN',\n        'BZD',\n        'CAD',\n        'CHF',\n        'CLP',\n        'CNY',\n        'COP',\n        'CRC',\n        'CUP',\n        'CVE',\n        'CZK',\n        'DJF',\n        'DKK',\n        'DOP',\n        'DZD',\n        'EGP',\n        'ETB',\n        'EUR',\n        'FJD',\n        'FKP',\n        'GBP',\n        'GEL',\n        'GHS',\n        'GIP',\n        'GMD',\n        'GNF',\n        'GTQ',\n        'GYD',\n        'HKD',\n        'HNL',\n        'HRK',\n        'HTG',\n        'HUF',\n        'IDR',\n        'ILS',\n        'INR',\n        'IQD',\n        'JMD',\n        'JOD',\n        'JPY',\n        'KES',\n        'KGS',\n        'KHR',\n        'KMF',\n        'KRW',\n        'KWD',\n        'KYD',\n        'KZT',\n        'LAK',\n        'LBP',\n        'LKR',\n        'LRD',\n        'LSL',\n        'LYD',\n        'MAD',\n        'MDL',\n        'MGA',\n        'MKD',\n        'MMK',\n        'MNT',\n        'MOP',\n        'MRU',\n        'MUR',\n        'MVR',\n        'MWK',\n        'MXN',\n        'MYR',\n        'MZN',\n        'NAD',\n        'NGN',\n        'NIO',\n        'NOK',\n        'NPR',\n        'NZD',\n        'OMR',\n        'PAB',\n        'PEN',\n        'PGK',\n        'PHP',\n        'PKR',\n        'PLN',\n        'PYG',\n        'QAR',\n        'RON',\n        'RSD',\n        'RUB',\n        'RWF',\n        'SAR',\n        'SBD',\n        'SCR',\n        'SEK',\n        'SGD',\n        'SHP',\n        'SLE',\n        'SLL',\n        'SOS',\n        'SRD',\n        'SSP',\n        'STN',\n        'SVC',\n        'SZL',\n        'THB',\n        'TND',\n        'TOP',\n        'TRY',\n        'TTD',\n        'TWD',\n        'TZS',\n        'UAH',\n        'UGX',\n        'USD',\n        'UYU',\n        'UZS',\n        'VES',\n        'VND',\n        'VUV',\n        'WST',\n        'XAF',\n        'XCD',\n        'XOF',\n        'XPF',\n        'YER',\n        'ZAR',\n        'ZMW'\n      ]\n    }\n  }\n}\n```",
  inputSchema: {
    type: 'object',
    properties: {
      customer_id: {
        type: 'string',
      },
      amount: {
        type: 'integer',
      },
      currency: {
        $ref: '#/$defs/currency',
      },
      entry_type: {
        type: 'string',
        description: 'Type of ledger entry - credit or debit',
        enum: ['credit', 'debit'],
      },
      idempotency_key: {
        type: 'string',
        description: 'Optional idempotency key to prevent duplicate entries',
      },
      reason: {
        type: 'string',
      },
      jq_filter: {
        type: 'string',
        title: 'jq Filter',
        description:
          'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
      },
    },
    required: ['customer_id', 'amount', 'currency', 'entry_type'],
    $defs: {
      currency: {
        type: 'string',
        enum: [
          'AED',
          'ALL',
          'AMD',
          'ANG',
          'AOA',
          'ARS',
          'AUD',
          'AWG',
          'AZN',
          'BAM',
          'BBD',
          'BDT',
          'BGN',
          'BHD',
          'BIF',
          'BMD',
          'BND',
          'BOB',
          'BRL',
          'BSD',
          'BWP',
          'BYN',
          'BZD',
          'CAD',
          'CHF',
          'CLP',
          'CNY',
          'COP',
          'CRC',
          'CUP',
          'CVE',
          'CZK',
          'DJF',
          'DKK',
          'DOP',
          'DZD',
          'EGP',
          'ETB',
          'EUR',
          'FJD',
          'FKP',
          'GBP',
          'GEL',
          'GHS',
          'GIP',
          'GMD',
          'GNF',
          'GTQ',
          'GYD',
          'HKD',
          'HNL',
          'HRK',
          'HTG',
          'HUF',
          'IDR',
          'ILS',
          'INR',
          'IQD',
          'JMD',
          'JOD',
          'JPY',
          'KES',
          'KGS',
          'KHR',
          'KMF',
          'KRW',
          'KWD',
          'KYD',
          'KZT',
          'LAK',
          'LBP',
          'LKR',
          'LRD',
          'LSL',
          'LYD',
          'MAD',
          'MDL',
          'MGA',
          'MKD',
          'MMK',
          'MNT',
          'MOP',
          'MRU',
          'MUR',
          'MVR',
          'MWK',
          'MXN',
          'MYR',
          'MZN',
          'NAD',
          'NGN',
          'NIO',
          'NOK',
          'NPR',
          'NZD',
          'OMR',
          'PAB',
          'PEN',
          'PGK',
          'PHP',
          'PKR',
          'PLN',
          'PYG',
          'QAR',
          'RON',
          'RSD',
          'RUB',
          'RWF',
          'SAR',
          'SBD',
          'SCR',
          'SEK',
          'SGD',
          'SHP',
          'SLE',
          'SLL',
          'SOS',
          'SRD',
          'SSP',
          'STN',
          'SVC',
          'SZL',
          'THB',
          'TND',
          'TOP',
          'TRY',
          'TTD',
          'TWD',
          'TZS',
          'UAH',
          'UGX',
          'USD',
          'UYU',
          'UZS',
          'VES',
          'VND',
          'VUV',
          'WST',
          'XAF',
          'XCD',
          'XOF',
          'XPF',
          'YER',
          'ZAR',
          'ZMW',
        ],
      },
    },
  },
  annotations: {},
};

export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
  const { customer_id, jq_filter, ...body } = args as any;
  return asTextContentResult(
    await maybeFilter(jq_filter, await client.customers.wallets.ledgerEntries.create(customer_id, body)),
  );
};

export default { metadata, tool, handler };

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/customers/wallets/ledger-entries/list-wallets-customers-ledger-entries.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { maybeFilter } from 'dodopayments-mcp/filtering';
import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import DodoPayments from 'dodopayments';

export const metadata: Metadata = {
  resource: 'customers.wallets.ledger_entries',
  operation: 'read',
  tags: [],
  httpMethod: 'get',
  httpPath: '/customers/{customer_id}/wallets/ledger-entries',
  operationId: 'get_customer_wallet_transactions',
};

export const tool: Tool = {
  name: 'list_wallets_customers_ledger_entries',
  description:
    "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n\n\n# Response Schema\n```json\n{\n  type: 'object',\n  properties: {\n    items: {\n      type: 'array',\n      items: {\n        $ref: '#/$defs/customer_wallet_transaction'\n      }\n    }\n  },\n  required: [    'items'\n  ],\n  $defs: {\n    customer_wallet_transaction: {\n      type: 'object',\n      properties: {\n        id: {\n          type: 'string'\n        },\n        after_balance: {\n          type: 'integer'\n        },\n        amount: {\n          type: 'integer'\n        },\n        before_balance: {\n          type: 'integer'\n        },\n        business_id: {\n          type: 'string'\n        },\n        created_at: {\n          type: 'string',\n          format: 'date-time'\n        },\n        currency: {\n          $ref: '#/$defs/currency'\n        },\n        customer_id: {\n          type: 'string'\n        },\n        event_type: {\n          type: 'string',\n          enum: [            'payment',\n            'payment_reversal',\n            'refund',\n            'refund_reversal',\n            'dispute',\n            'dispute_reversal',\n            'merchant_adjustment'\n          ]\n        },\n        is_credit: {\n          type: 'boolean'\n        },\n        reason: {\n          type: 'string'\n        },\n        reference_object_id: {\n          type: 'string'\n        }\n      },\n      required: [        'id',\n        'after_balance',\n        'amount',\n        'before_balance',\n        'business_id',\n        'created_at',\n        'currency',\n        'customer_id',\n        'event_type',\n        'is_credit'\n      ]\n    },\n    currency: {\n      type: 'string',\n      enum: [        'AED',\n        'ALL',\n        'AMD',\n        'ANG',\n        'AOA',\n        'ARS',\n        'AUD',\n        'AWG',\n        'AZN',\n        'BAM',\n        'BBD',\n        'BDT',\n        'BGN',\n        'BHD',\n        'BIF',\n        'BMD',\n        'BND',\n        'BOB',\n        'BRL',\n        'BSD',\n        'BWP',\n        'BYN',\n        'BZD',\n        'CAD',\n        'CHF',\n        'CLP',\n        'CNY',\n        'COP',\n        'CRC',\n        'CUP',\n        'CVE',\n        'CZK',\n        'DJF',\n        'DKK',\n        'DOP',\n        'DZD',\n        'EGP',\n        'ETB',\n        'EUR',\n        'FJD',\n        'FKP',\n        'GBP',\n        'GEL',\n        'GHS',\n        'GIP',\n        'GMD',\n        'GNF',\n        'GTQ',\n        'GYD',\n        'HKD',\n        'HNL',\n        'HRK',\n        'HTG',\n        'HUF',\n        'IDR',\n        'ILS',\n        'INR',\n        'IQD',\n        'JMD',\n        'JOD',\n        'JPY',\n        'KES',\n        'KGS',\n        'KHR',\n        'KMF',\n        'KRW',\n        'KWD',\n        'KYD',\n        'KZT',\n        'LAK',\n        'LBP',\n        'LKR',\n        'LRD',\n        'LSL',\n        'LYD',\n        'MAD',\n        'MDL',\n        'MGA',\n        'MKD',\n        'MMK',\n        'MNT',\n        'MOP',\n        'MRU',\n        'MUR',\n        'MVR',\n        'MWK',\n        'MXN',\n        'MYR',\n        'MZN',\n        'NAD',\n        'NGN',\n        'NIO',\n        'NOK',\n        'NPR',\n        'NZD',\n        'OMR',\n        'PAB',\n        'PEN',\n        'PGK',\n        'PHP',\n        'PKR',\n        'PLN',\n        'PYG',\n        'QAR',\n        'RON',\n        'RSD',\n        'RUB',\n        'RWF',\n        'SAR',\n        'SBD',\n        'SCR',\n        'SEK',\n        'SGD',\n        'SHP',\n        'SLE',\n        'SLL',\n        'SOS',\n        'SRD',\n        'SSP',\n        'STN',\n        'SVC',\n        'SZL',\n        'THB',\n        'TND',\n        'TOP',\n        'TRY',\n        'TTD',\n        'TWD',\n        'TZS',\n        'UAH',\n        'UGX',\n        'USD',\n        'UYU',\n        'UZS',\n        'VES',\n        'VND',\n        'VUV',\n        'WST',\n        'XAF',\n        'XCD',\n        'XOF',\n        'XPF',\n        'YER',\n        'ZAR',\n        'ZMW'\n      ]\n    }\n  }\n}\n```",
  inputSchema: {
    type: 'object',
    properties: {
      customer_id: {
        type: 'string',
      },
      currency: {
        $ref: '#/$defs/currency',
      },
      page_number: {
        type: 'integer',
      },
      page_size: {
        type: 'integer',
      },
      jq_filter: {
        type: 'string',
        title: 'jq Filter',
        description:
          'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
      },
    },
    required: ['customer_id'],
    $defs: {
      currency: {
        type: 'string',
        enum: [
          'AED',
          'ALL',
          'AMD',
          'ANG',
          'AOA',
          'ARS',
          'AUD',
          'AWG',
          'AZN',
          'BAM',
          'BBD',
          'BDT',
          'BGN',
          'BHD',
          'BIF',
          'BMD',
          'BND',
          'BOB',
          'BRL',
          'BSD',
          'BWP',
          'BYN',
          'BZD',
          'CAD',
          'CHF',
          'CLP',
          'CNY',
          'COP',
          'CRC',
          'CUP',
          'CVE',
          'CZK',
          'DJF',
          'DKK',
          'DOP',
          'DZD',
          'EGP',
          'ETB',
          'EUR',
          'FJD',
          'FKP',
          'GBP',
          'GEL',
          'GHS',
          'GIP',
          'GMD',
          'GNF',
          'GTQ',
          'GYD',
          'HKD',
          'HNL',
          'HRK',
          'HTG',
          'HUF',
          'IDR',
          'ILS',
          'INR',
          'IQD',
          'JMD',
          'JOD',
          'JPY',
          'KES',
          'KGS',
          'KHR',
          'KMF',
          'KRW',
          'KWD',
          'KYD',
          'KZT',
          'LAK',
          'LBP',
          'LKR',
          'LRD',
          'LSL',
          'LYD',
          'MAD',
          'MDL',
          'MGA',
          'MKD',
          'MMK',
          'MNT',
          'MOP',
          'MRU',
          'MUR',
          'MVR',
          'MWK',
          'MXN',
          'MYR',
          'MZN',
          'NAD',
          'NGN',
          'NIO',
          'NOK',
          'NPR',
          'NZD',
          'OMR',
          'PAB',
          'PEN',
          'PGK',
          'PHP',
          'PKR',
          'PLN',
          'PYG',
          'QAR',
          'RON',
          'RSD',
          'RUB',
          'RWF',
          'SAR',
          'SBD',
          'SCR',
          'SEK',
          'SGD',
          'SHP',
          'SLE',
          'SLL',
          'SOS',
          'SRD',
          'SSP',
          'STN',
          'SVC',
          'SZL',
          'THB',
          'TND',
          'TOP',
          'TRY',
          'TTD',
          'TWD',
          'TZS',
          'UAH',
          'UGX',
          'USD',
          'UYU',
          'UZS',
          'VES',
          'VND',
          'VUV',
          'WST',
          'XAF',
          'XCD',
          'XOF',
          'XPF',
          'YER',
          'ZAR',
          'ZMW',
        ],
      },
    },
  },
  annotations: {
    readOnlyHint: true,
  },
};

export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
  const { customer_id, jq_filter, ...body } = args as any;
  const response = await client.customers.wallets.ledgerEntries.list(customer_id, body).asResponse();
  return asTextContentResult(await maybeFilter(jq_filter, await response.json()));
};

export default { metadata, tool, handler };

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/addons/create-addons.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { maybeFilter } from 'dodopayments-mcp/filtering';
import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import DodoPayments from 'dodopayments';

export const metadata: Metadata = {
  resource: 'addons',
  operation: 'write',
  tags: [],
  httpMethod: 'post',
  httpPath: '/addons',
  operationId: 'create_addon',
};

export const tool: Tool = {
  name: 'create_addons',
  description:
    "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n\n\n# Response Schema\n```json\n{\n  $ref: '#/$defs/addon_response',\n  $defs: {\n    addon_response: {\n      type: 'object',\n      properties: {\n        id: {\n          type: 'string',\n          description: 'id of the Addon'\n        },\n        business_id: {\n          type: 'string',\n          description: 'Unique identifier for the business to which the addon belongs.'\n        },\n        created_at: {\n          type: 'string',\n          description: 'Created time',\n          format: 'date-time'\n        },\n        currency: {\n          $ref: '#/$defs/currency'\n        },\n        name: {\n          type: 'string',\n          description: 'Name of the Addon'\n        },\n        price: {\n          type: 'integer',\n          description: 'Amount of the addon'\n        },\n        tax_category: {\n          $ref: '#/$defs/tax_category'\n        },\n        updated_at: {\n          type: 'string',\n          description: 'Updated time',\n          format: 'date-time'\n        },\n        description: {\n          type: 'string',\n          description: 'Optional description of the Addon'\n        },\n        image: {\n          type: 'string',\n          description: 'Image of the Addon'\n        }\n      },\n      required: [        'id',\n        'business_id',\n        'created_at',\n        'currency',\n        'name',\n        'price',\n        'tax_category',\n        'updated_at'\n      ]\n    },\n    currency: {\n      type: 'string',\n      enum: [        'AED',\n        'ALL',\n        'AMD',\n        'ANG',\n        'AOA',\n        'ARS',\n        'AUD',\n        'AWG',\n        'AZN',\n        'BAM',\n        'BBD',\n        'BDT',\n        'BGN',\n        'BHD',\n        'BIF',\n        'BMD',\n        'BND',\n        'BOB',\n        'BRL',\n        'BSD',\n        'BWP',\n        'BYN',\n        'BZD',\n        'CAD',\n        'CHF',\n        'CLP',\n        'CNY',\n        'COP',\n        'CRC',\n        'CUP',\n        'CVE',\n        'CZK',\n        'DJF',\n        'DKK',\n        'DOP',\n        'DZD',\n        'EGP',\n        'ETB',\n        'EUR',\n        'FJD',\n        'FKP',\n        'GBP',\n        'GEL',\n        'GHS',\n        'GIP',\n        'GMD',\n        'GNF',\n        'GTQ',\n        'GYD',\n        'HKD',\n        'HNL',\n        'HRK',\n        'HTG',\n        'HUF',\n        'IDR',\n        'ILS',\n        'INR',\n        'IQD',\n        'JMD',\n        'JOD',\n        'JPY',\n        'KES',\n        'KGS',\n        'KHR',\n        'KMF',\n        'KRW',\n        'KWD',\n        'KYD',\n        'KZT',\n        'LAK',\n        'LBP',\n        'LKR',\n        'LRD',\n        'LSL',\n        'LYD',\n        'MAD',\n        'MDL',\n        'MGA',\n        'MKD',\n        'MMK',\n        'MNT',\n        'MOP',\n        'MRU',\n        'MUR',\n        'MVR',\n        'MWK',\n        'MXN',\n        'MYR',\n        'MZN',\n        'NAD',\n        'NGN',\n        'NIO',\n        'NOK',\n        'NPR',\n        'NZD',\n        'OMR',\n        'PAB',\n        'PEN',\n        'PGK',\n        'PHP',\n        'PKR',\n        'PLN',\n        'PYG',\n        'QAR',\n        'RON',\n        'RSD',\n        'RUB',\n        'RWF',\n        'SAR',\n        'SBD',\n        'SCR',\n        'SEK',\n        'SGD',\n        'SHP',\n        'SLE',\n        'SLL',\n        'SOS',\n        'SRD',\n        'SSP',\n        'STN',\n        'SVC',\n        'SZL',\n        'THB',\n        'TND',\n        'TOP',\n        'TRY',\n        'TTD',\n        'TWD',\n        'TZS',\n        'UAH',\n        'UGX',\n        'USD',\n        'UYU',\n        'UZS',\n        'VES',\n        'VND',\n        'VUV',\n        'WST',\n        'XAF',\n        'XCD',\n        'XOF',\n        'XPF',\n        'YER',\n        'ZAR',\n        'ZMW'\n      ]\n    },\n    tax_category: {\n      type: 'string',\n      description: 'Represents the different categories of taxation applicable to various products and services.',\n      enum: [        'digital_products',\n        'saas',\n        'e_book',\n        'edtech'\n      ]\n    }\n  }\n}\n```",
  inputSchema: {
    type: 'object',
    properties: {
      currency: {
        $ref: '#/$defs/currency',
      },
      name: {
        type: 'string',
        description: 'Name of the Addon',
      },
      price: {
        type: 'integer',
        description: 'Amount of the addon',
      },
      tax_category: {
        $ref: '#/$defs/tax_category',
      },
      description: {
        type: 'string',
        description: 'Optional description of the Addon',
      },
      jq_filter: {
        type: 'string',
        title: 'jq Filter',
        description:
          'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
      },
    },
    required: ['currency', 'name', 'price', 'tax_category'],
    $defs: {
      currency: {
        type: 'string',
        enum: [
          'AED',
          'ALL',
          'AMD',
          'ANG',
          'AOA',
          'ARS',
          'AUD',
          'AWG',
          'AZN',
          'BAM',
          'BBD',
          'BDT',
          'BGN',
          'BHD',
          'BIF',
          'BMD',
          'BND',
          'BOB',
          'BRL',
          'BSD',
          'BWP',
          'BYN',
          'BZD',
          'CAD',
          'CHF',
          'CLP',
          'CNY',
          'COP',
          'CRC',
          'CUP',
          'CVE',
          'CZK',
          'DJF',
          'DKK',
          'DOP',
          'DZD',
          'EGP',
          'ETB',
          'EUR',
          'FJD',
          'FKP',
          'GBP',
          'GEL',
          'GHS',
          'GIP',
          'GMD',
          'GNF',
          'GTQ',
          'GYD',
          'HKD',
          'HNL',
          'HRK',
          'HTG',
          'HUF',
          'IDR',
          'ILS',
          'INR',
          'IQD',
          'JMD',
          'JOD',
          'JPY',
          'KES',
          'KGS',
          'KHR',
          'KMF',
          'KRW',
          'KWD',
          'KYD',
          'KZT',
          'LAK',
          'LBP',
          'LKR',
          'LRD',
          'LSL',
          'LYD',
          'MAD',
          'MDL',
          'MGA',
          'MKD',
          'MMK',
          'MNT',
          'MOP',
          'MRU',
          'MUR',
          'MVR',
          'MWK',
          'MXN',
          'MYR',
          'MZN',
          'NAD',
          'NGN',
          'NIO',
          'NOK',
          'NPR',
          'NZD',
          'OMR',
          'PAB',
          'PEN',
          'PGK',
          'PHP',
          'PKR',
          'PLN',
          'PYG',
          'QAR',
          'RON',
          'RSD',
          'RUB',
          'RWF',
          'SAR',
          'SBD',
          'SCR',
          'SEK',
          'SGD',
          'SHP',
          'SLE',
          'SLL',
          'SOS',
          'SRD',
          'SSP',
          'STN',
          'SVC',
          'SZL',
          'THB',
          'TND',
          'TOP',
          'TRY',
          'TTD',
          'TWD',
          'TZS',
          'UAH',
          'UGX',
          'USD',
          'UYU',
          'UZS',
          'VES',
          'VND',
          'VUV',
          'WST',
          'XAF',
          'XCD',
          'XOF',
          'XPF',
          'YER',
          'ZAR',
          'ZMW',
        ],
      },
      tax_category: {
        type: 'string',
        description:
          'Represents the different categories of taxation applicable to various products and services.',
        enum: ['digital_products', 'saas', 'e_book', 'edtech'],
      },
    },
  },
  annotations: {},
};

export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
  const { jq_filter, ...body } = args as any;
  return asTextContentResult(await maybeFilter(jq_filter, await client.addons.create(body)));
};

export default { metadata, tool, handler };

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/subscriptions/retrieve-usage-history-subscriptions.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { maybeFilter } from 'dodopayments-mcp/filtering';
import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import DodoPayments from 'dodopayments';

export const metadata: Metadata = {
  resource: 'subscriptions',
  operation: 'read',
  tags: [],
  httpMethod: 'get',
  httpPath: '/subscriptions/{subscription_id}/usage-history',
  operationId: 'list_usage_history_handler',
};

export const tool: Tool = {
  name: 'retrieve_usage_history_subscriptions',
  description:
    "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nGet detailed usage history for a subscription that includes usage-based billing (metered components).\nThis endpoint provides insights into customer usage patterns and billing calculations over time.\n\n## What You'll Get:\n- **Billing periods**: Each item represents a billing cycle with start and end dates\n- **Meter usage**: Detailed breakdown of usage for each meter configured on the subscription\n- **Usage calculations**: Total units consumed, free threshold units, and chargeable units\n- **Historical tracking**: Complete audit trail of usage-based charges\n\n## Use Cases:\n- **Customer support**: Investigate billing questions and usage discrepancies\n- **Usage analytics**: Analyze customer consumption patterns over time\n- **Billing transparency**: Provide customers with detailed usage breakdowns\n- **Revenue optimization**: Identify usage trends to optimize pricing strategies\n\n## Filtering Options:\n- **Date range filtering**: Get usage history for specific time periods\n- **Meter-specific filtering**: Focus on usage for a particular meter\n- **Pagination**: Navigate through large usage histories efficiently\n\n## Important Notes:\n- Only returns data for subscriptions with usage-based (metered) components\n- Usage history is organized by billing periods (subscription cycles)\n- Free threshold units are calculated and displayed separately from chargeable units\n- Historical data is preserved even if meter configurations change\n\n## Example Query Patterns:\n- Get last 3 months: `?start_date=2024-01-01T00:00:00Z&end_date=2024-03-31T23:59:59Z`\n- Filter by meter: `?meter_id=mtr_api_requests`\n- Paginate results: `?page_size=20&page_number=1`\n- Recent usage: `?start_date=2024-03-01T00:00:00Z` (from March 1st to now)\n\n# Response Schema\n```json\n{\n  type: 'object',\n  properties: {\n    items: {\n      type: 'array',\n      description: 'List of usage history items',\n      items: {\n        $ref: '#/$defs/subscription_retrieve_usage_history_response'\n      }\n    }\n  },\n  required: [    'items'\n  ],\n  $defs: {\n    subscription_retrieve_usage_history_response: {\n      type: 'object',\n      properties: {\n        end_date: {\n          type: 'string',\n          description: 'End date of the billing period',\n          format: 'date-time'\n        },\n        meters: {\n          type: 'array',\n          description: 'List of meters and their usage for this billing period',\n          items: {\n            type: 'object',\n            properties: {\n              id: {\n                type: 'string',\n                description: 'Meter identifier'\n              },\n              chargeable_units: {\n                type: 'string',\n                description: 'Chargeable units (after free threshold) as string for precision'\n              },\n              consumed_units: {\n                type: 'string',\n                description: 'Total units consumed as string for precision'\n              },\n              currency: {\n                $ref: '#/$defs/currency'\n              },\n              free_threshold: {\n                type: 'integer',\n                description: 'Free threshold units for this meter'\n              },\n              name: {\n                type: 'string',\n                description: 'Meter name'\n              },\n              price_per_unit: {\n                type: 'string',\n                description: 'Price per unit in string format for precision'\n              },\n              total_price: {\n                type: 'integer',\n                description: 'Total price charged for this meter in smallest currency unit (cents)'\n              }\n            },\n            required: [              'id',\n              'chargeable_units',\n              'consumed_units',\n              'currency',\n              'free_threshold',\n              'name',\n              'price_per_unit',\n              'total_price'\n            ]\n          }\n        },\n        start_date: {\n          type: 'string',\n          description: 'Start date of the billing period',\n          format: 'date-time'\n        }\n      },\n      required: [        'end_date',\n        'meters',\n        'start_date'\n      ]\n    },\n    currency: {\n      type: 'string',\n      enum: [        'AED',\n        'ALL',\n        'AMD',\n        'ANG',\n        'AOA',\n        'ARS',\n        'AUD',\n        'AWG',\n        'AZN',\n        'BAM',\n        'BBD',\n        'BDT',\n        'BGN',\n        'BHD',\n        'BIF',\n        'BMD',\n        'BND',\n        'BOB',\n        'BRL',\n        'BSD',\n        'BWP',\n        'BYN',\n        'BZD',\n        'CAD',\n        'CHF',\n        'CLP',\n        'CNY',\n        'COP',\n        'CRC',\n        'CUP',\n        'CVE',\n        'CZK',\n        'DJF',\n        'DKK',\n        'DOP',\n        'DZD',\n        'EGP',\n        'ETB',\n        'EUR',\n        'FJD',\n        'FKP',\n        'GBP',\n        'GEL',\n        'GHS',\n        'GIP',\n        'GMD',\n        'GNF',\n        'GTQ',\n        'GYD',\n        'HKD',\n        'HNL',\n        'HRK',\n        'HTG',\n        'HUF',\n        'IDR',\n        'ILS',\n        'INR',\n        'IQD',\n        'JMD',\n        'JOD',\n        'JPY',\n        'KES',\n        'KGS',\n        'KHR',\n        'KMF',\n        'KRW',\n        'KWD',\n        'KYD',\n        'KZT',\n        'LAK',\n        'LBP',\n        'LKR',\n        'LRD',\n        'LSL',\n        'LYD',\n        'MAD',\n        'MDL',\n        'MGA',\n        'MKD',\n        'MMK',\n        'MNT',\n        'MOP',\n        'MRU',\n        'MUR',\n        'MVR',\n        'MWK',\n        'MXN',\n        'MYR',\n        'MZN',\n        'NAD',\n        'NGN',\n        'NIO',\n        'NOK',\n        'NPR',\n        'NZD',\n        'OMR',\n        'PAB',\n        'PEN',\n        'PGK',\n        'PHP',\n        'PKR',\n        'PLN',\n        'PYG',\n        'QAR',\n        'RON',\n        'RSD',\n        'RUB',\n        'RWF',\n        'SAR',\n        'SBD',\n        'SCR',\n        'SEK',\n        'SGD',\n        'SHP',\n        'SLE',\n        'SLL',\n        'SOS',\n        'SRD',\n        'SSP',\n        'STN',\n        'SVC',\n        'SZL',\n        'THB',\n        'TND',\n        'TOP',\n        'TRY',\n        'TTD',\n        'TWD',\n        'TZS',\n        'UAH',\n        'UGX',\n        'USD',\n        'UYU',\n        'UZS',\n        'VES',\n        'VND',\n        'VUV',\n        'WST',\n        'XAF',\n        'XCD',\n        'XOF',\n        'XPF',\n        'YER',\n        'ZAR',\n        'ZMW'\n      ]\n    }\n  }\n}\n```",
  inputSchema: {
    type: 'object',
    properties: {
      subscription_id: {
        type: 'string',
      },
      end_date: {
        type: 'string',
        description: 'Filter by end date (inclusive)',
        format: 'date-time',
      },
      meter_id: {
        type: 'string',
        description: 'Filter by specific meter ID',
      },
      page_number: {
        type: 'integer',
        description: 'Page number (default: 0)',
      },
      page_size: {
        type: 'integer',
        description: 'Page size (default: 10, max: 100)',
      },
      start_date: {
        type: 'string',
        description: 'Filter by start date (inclusive)',
        format: 'date-time',
      },
      jq_filter: {
        type: 'string',
        title: 'jq Filter',
        description:
          'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
      },
    },
    required: ['subscription_id'],
  },
  annotations: {
    readOnlyHint: true,
  },
};

export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
  const { subscription_id, jq_filter, ...body } = args as any;
  const response = await client.subscriptions.retrieveUsageHistory(subscription_id, body).asResponse();
  return asTextContentResult(await maybeFilter(jq_filter, await response.json()));
};

export default { metadata, tool, handler };

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/addons/update-addons.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { maybeFilter } from 'dodopayments-mcp/filtering';
import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import DodoPayments from 'dodopayments';

export const metadata: Metadata = {
  resource: 'addons',
  operation: 'write',
  tags: [],
  httpMethod: 'patch',
  httpPath: '/addons/{id}',
  operationId: 'patch_addon',
};

export const tool: Tool = {
  name: 'update_addons',
  description:
    "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n\n\n# Response Schema\n```json\n{\n  $ref: '#/$defs/addon_response',\n  $defs: {\n    addon_response: {\n      type: 'object',\n      properties: {\n        id: {\n          type: 'string',\n          description: 'id of the Addon'\n        },\n        business_id: {\n          type: 'string',\n          description: 'Unique identifier for the business to which the addon belongs.'\n        },\n        created_at: {\n          type: 'string',\n          description: 'Created time',\n          format: 'date-time'\n        },\n        currency: {\n          $ref: '#/$defs/currency'\n        },\n        name: {\n          type: 'string',\n          description: 'Name of the Addon'\n        },\n        price: {\n          type: 'integer',\n          description: 'Amount of the addon'\n        },\n        tax_category: {\n          $ref: '#/$defs/tax_category'\n        },\n        updated_at: {\n          type: 'string',\n          description: 'Updated time',\n          format: 'date-time'\n        },\n        description: {\n          type: 'string',\n          description: 'Optional description of the Addon'\n        },\n        image: {\n          type: 'string',\n          description: 'Image of the Addon'\n        }\n      },\n      required: [        'id',\n        'business_id',\n        'created_at',\n        'currency',\n        'name',\n        'price',\n        'tax_category',\n        'updated_at'\n      ]\n    },\n    currency: {\n      type: 'string',\n      enum: [        'AED',\n        'ALL',\n        'AMD',\n        'ANG',\n        'AOA',\n        'ARS',\n        'AUD',\n        'AWG',\n        'AZN',\n        'BAM',\n        'BBD',\n        'BDT',\n        'BGN',\n        'BHD',\n        'BIF',\n        'BMD',\n        'BND',\n        'BOB',\n        'BRL',\n        'BSD',\n        'BWP',\n        'BYN',\n        'BZD',\n        'CAD',\n        'CHF',\n        'CLP',\n        'CNY',\n        'COP',\n        'CRC',\n        'CUP',\n        'CVE',\n        'CZK',\n        'DJF',\n        'DKK',\n        'DOP',\n        'DZD',\n        'EGP',\n        'ETB',\n        'EUR',\n        'FJD',\n        'FKP',\n        'GBP',\n        'GEL',\n        'GHS',\n        'GIP',\n        'GMD',\n        'GNF',\n        'GTQ',\n        'GYD',\n        'HKD',\n        'HNL',\n        'HRK',\n        'HTG',\n        'HUF',\n        'IDR',\n        'ILS',\n        'INR',\n        'IQD',\n        'JMD',\n        'JOD',\n        'JPY',\n        'KES',\n        'KGS',\n        'KHR',\n        'KMF',\n        'KRW',\n        'KWD',\n        'KYD',\n        'KZT',\n        'LAK',\n        'LBP',\n        'LKR',\n        'LRD',\n        'LSL',\n        'LYD',\n        'MAD',\n        'MDL',\n        'MGA',\n        'MKD',\n        'MMK',\n        'MNT',\n        'MOP',\n        'MRU',\n        'MUR',\n        'MVR',\n        'MWK',\n        'MXN',\n        'MYR',\n        'MZN',\n        'NAD',\n        'NGN',\n        'NIO',\n        'NOK',\n        'NPR',\n        'NZD',\n        'OMR',\n        'PAB',\n        'PEN',\n        'PGK',\n        'PHP',\n        'PKR',\n        'PLN',\n        'PYG',\n        'QAR',\n        'RON',\n        'RSD',\n        'RUB',\n        'RWF',\n        'SAR',\n        'SBD',\n        'SCR',\n        'SEK',\n        'SGD',\n        'SHP',\n        'SLE',\n        'SLL',\n        'SOS',\n        'SRD',\n        'SSP',\n        'STN',\n        'SVC',\n        'SZL',\n        'THB',\n        'TND',\n        'TOP',\n        'TRY',\n        'TTD',\n        'TWD',\n        'TZS',\n        'UAH',\n        'UGX',\n        'USD',\n        'UYU',\n        'UZS',\n        'VES',\n        'VND',\n        'VUV',\n        'WST',\n        'XAF',\n        'XCD',\n        'XOF',\n        'XPF',\n        'YER',\n        'ZAR',\n        'ZMW'\n      ]\n    },\n    tax_category: {\n      type: 'string',\n      description: 'Represents the different categories of taxation applicable to various products and services.',\n      enum: [        'digital_products',\n        'saas',\n        'e_book',\n        'edtech'\n      ]\n    }\n  }\n}\n```",
  inputSchema: {
    type: 'object',
    properties: {
      id: {
        type: 'string',
      },
      currency: {
        $ref: '#/$defs/currency',
      },
      description: {
        type: 'string',
        description: 'Description of the Addon, optional and must be at most 1000 characters.',
      },
      image_id: {
        type: 'string',
        description: 'Addon image id after its uploaded to S3',
      },
      name: {
        type: 'string',
        description: 'Name of the Addon, optional and must be at most 100 characters.',
      },
      price: {
        type: 'integer',
        description: 'Amount of the addon',
      },
      tax_category: {
        $ref: '#/$defs/tax_category',
      },
      jq_filter: {
        type: 'string',
        title: 'jq Filter',
        description:
          'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
      },
    },
    required: ['id'],
    $defs: {
      currency: {
        type: 'string',
        enum: [
          'AED',
          'ALL',
          'AMD',
          'ANG',
          'AOA',
          'ARS',
          'AUD',
          'AWG',
          'AZN',
          'BAM',
          'BBD',
          'BDT',
          'BGN',
          'BHD',
          'BIF',
          'BMD',
          'BND',
          'BOB',
          'BRL',
          'BSD',
          'BWP',
          'BYN',
          'BZD',
          'CAD',
          'CHF',
          'CLP',
          'CNY',
          'COP',
          'CRC',
          'CUP',
          'CVE',
          'CZK',
          'DJF',
          'DKK',
          'DOP',
          'DZD',
          'EGP',
          'ETB',
          'EUR',
          'FJD',
          'FKP',
          'GBP',
          'GEL',
          'GHS',
          'GIP',
          'GMD',
          'GNF',
          'GTQ',
          'GYD',
          'HKD',
          'HNL',
          'HRK',
          'HTG',
          'HUF',
          'IDR',
          'ILS',
          'INR',
          'IQD',
          'JMD',
          'JOD',
          'JPY',
          'KES',
          'KGS',
          'KHR',
          'KMF',
          'KRW',
          'KWD',
          'KYD',
          'KZT',
          'LAK',
          'LBP',
          'LKR',
          'LRD',
          'LSL',
          'LYD',
          'MAD',
          'MDL',
          'MGA',
          'MKD',
          'MMK',
          'MNT',
          'MOP',
          'MRU',
          'MUR',
          'MVR',
          'MWK',
          'MXN',
          'MYR',
          'MZN',
          'NAD',
          'NGN',
          'NIO',
          'NOK',
          'NPR',
          'NZD',
          'OMR',
          'PAB',
          'PEN',
          'PGK',
          'PHP',
          'PKR',
          'PLN',
          'PYG',
          'QAR',
          'RON',
          'RSD',
          'RUB',
          'RWF',
          'SAR',
          'SBD',
          'SCR',
          'SEK',
          'SGD',
          'SHP',
          'SLE',
          'SLL',
          'SOS',
          'SRD',
          'SSP',
          'STN',
          'SVC',
          'SZL',
          'THB',
          'TND',
          'TOP',
          'TRY',
          'TTD',
          'TWD',
          'TZS',
          'UAH',
          'UGX',
          'USD',
          'UYU',
          'UZS',
          'VES',
          'VND',
          'VUV',
          'WST',
          'XAF',
          'XCD',
          'XOF',
          'XPF',
          'YER',
          'ZAR',
          'ZMW',
        ],
      },
      tax_category: {
        type: 'string',
        description:
          'Represents the different categories of taxation applicable to various products and services.',
        enum: ['digital_products', 'saas', 'e_book', 'edtech'],
      },
    },
  },
  annotations: {},
};

export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
  const { id, jq_filter, ...body } = args as any;
  return asTextContentResult(await maybeFilter(jq_filter, await client.addons.update(id, body)));
};

export default { metadata, tool, handler };

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/index.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { Metadata, Endpoint, HandlerFunction } from './types';

export { Metadata, Endpoint, HandlerFunction };

import create_checkout_sessions from './checkout-sessions/create-checkout-sessions';
import retrieve_checkout_sessions from './checkout-sessions/retrieve-checkout-sessions';
import create_payments from './payments/create-payments';
import retrieve_payments from './payments/retrieve-payments';
import list_payments from './payments/list-payments';
import retrieve_line_items_payments from './payments/retrieve-line-items-payments';
import create_subscriptions from './subscriptions/create-subscriptions';
import retrieve_subscriptions from './subscriptions/retrieve-subscriptions';
import update_subscriptions from './subscriptions/update-subscriptions';
import list_subscriptions from './subscriptions/list-subscriptions';
import change_plan_subscriptions from './subscriptions/change-plan-subscriptions';
import charge_subscriptions from './subscriptions/charge-subscriptions';
import retrieve_usage_history_subscriptions from './subscriptions/retrieve-usage-history-subscriptions';
import retrieve_invoices_payments from './invoices/payments/retrieve-invoices-payments';
import retrieve_refund_invoices_payments from './invoices/payments/retrieve-refund-invoices-payments';
import activate_licenses from './licenses/activate-licenses';
import deactivate_licenses from './licenses/deactivate-licenses';
import validate_licenses from './licenses/validate-licenses';
import retrieve_license_keys from './license-keys/retrieve-license-keys';
import update_license_keys from './license-keys/update-license-keys';
import list_license_keys from './license-keys/list-license-keys';
import retrieve_license_key_instances from './license-key-instances/retrieve-license-key-instances';
import update_license_key_instances from './license-key-instances/update-license-key-instances';
import list_license_key_instances from './license-key-instances/list-license-key-instances';
import create_customers from './customers/create-customers';
import retrieve_customers from './customers/retrieve-customers';
import update_customers from './customers/update-customers';
import list_customers from './customers/list-customers';
import create_customers_customer_portal from './customers/customer-portal/create-customers-customer-portal';
import list_customers_wallets from './customers/wallets/list-customers-wallets';
import create_wallets_customers_ledger_entries from './customers/wallets/ledger-entries/create-wallets-customers-ledger-entries';
import list_wallets_customers_ledger_entries from './customers/wallets/ledger-entries/list-wallets-customers-ledger-entries';
import create_refunds from './refunds/create-refunds';
import retrieve_refunds from './refunds/retrieve-refunds';
import list_refunds from './refunds/list-refunds';
import retrieve_disputes from './disputes/retrieve-disputes';
import list_disputes from './disputes/list-disputes';
import list_payouts from './payouts/list-payouts';
import create_products from './products/create-products';
import retrieve_products from './products/retrieve-products';
import update_products from './products/update-products';
import list_products from './products/list-products';
import archive_products from './products/archive-products';
import unarchive_products from './products/unarchive-products';
import update_files_products from './products/update-files-products';
import update_products_images from './products/images/update-products-images';
import list_supported_countries_misc from './misc/list-supported-countries-misc';
import create_discounts from './discounts/create-discounts';
import retrieve_discounts from './discounts/retrieve-discounts';
import update_discounts from './discounts/update-discounts';
import list_discounts from './discounts/list-discounts';
import delete_discounts from './discounts/delete-discounts';
import create_addons from './addons/create-addons';
import retrieve_addons from './addons/retrieve-addons';
import update_addons from './addons/update-addons';
import list_addons from './addons/list-addons';
import update_images_addons from './addons/update-images-addons';
import create_brands from './brands/create-brands';
import retrieve_brands from './brands/retrieve-brands';
import update_brands from './brands/update-brands';
import list_brands from './brands/list-brands';
import update_images_brands from './brands/update-images-brands';
import create_webhooks from './webhooks/create-webhooks';
import retrieve_webhooks from './webhooks/retrieve-webhooks';
import update_webhooks from './webhooks/update-webhooks';
import list_webhooks from './webhooks/list-webhooks';
import delete_webhooks from './webhooks/delete-webhooks';
import retrieve_secret_webhooks from './webhooks/retrieve-secret-webhooks';
import retrieve_webhooks_headers from './webhooks/headers/retrieve-webhooks-headers';
import update_webhooks_headers from './webhooks/headers/update-webhooks-headers';
import retrieve_usage_events from './usage-events/retrieve-usage-events';
import list_usage_events from './usage-events/list-usage-events';
import ingest_usage_events from './usage-events/ingest-usage-events';
import create_meters from './meters/create-meters';
import retrieve_meters from './meters/retrieve-meters';
import list_meters from './meters/list-meters';
import archive_meters from './meters/archive-meters';
import unarchive_meters from './meters/unarchive-meters';

export const endpoints: Endpoint[] = [];

function addEndpoint(endpoint: Endpoint) {
  endpoints.push(endpoint);
}

addEndpoint(create_checkout_sessions);
addEndpoint(retrieve_checkout_sessions);
addEndpoint(create_payments);
addEndpoint(retrieve_payments);
addEndpoint(list_payments);
addEndpoint(retrieve_line_items_payments);
addEndpoint(create_subscriptions);
addEndpoint(retrieve_subscriptions);
addEndpoint(update_subscriptions);
addEndpoint(list_subscriptions);
addEndpoint(change_plan_subscriptions);
addEndpoint(charge_subscriptions);
addEndpoint(retrieve_usage_history_subscriptions);
addEndpoint(retrieve_invoices_payments);
addEndpoint(retrieve_refund_invoices_payments);
addEndpoint(activate_licenses);
addEndpoint(deactivate_licenses);
addEndpoint(validate_licenses);
addEndpoint(retrieve_license_keys);
addEndpoint(update_license_keys);
addEndpoint(list_license_keys);
addEndpoint(retrieve_license_key_instances);
addEndpoint(update_license_key_instances);
addEndpoint(list_license_key_instances);
addEndpoint(create_customers);
addEndpoint(retrieve_customers);
addEndpoint(update_customers);
addEndpoint(list_customers);
addEndpoint(create_customers_customer_portal);
addEndpoint(list_customers_wallets);
addEndpoint(create_wallets_customers_ledger_entries);
addEndpoint(list_wallets_customers_ledger_entries);
addEndpoint(create_refunds);
addEndpoint(retrieve_refunds);
addEndpoint(list_refunds);
addEndpoint(retrieve_disputes);
addEndpoint(list_disputes);
addEndpoint(list_payouts);
addEndpoint(create_products);
addEndpoint(retrieve_products);
addEndpoint(update_products);
addEndpoint(list_products);
addEndpoint(archive_products);
addEndpoint(unarchive_products);
addEndpoint(update_files_products);
addEndpoint(update_products_images);
addEndpoint(list_supported_countries_misc);
addEndpoint(create_discounts);
addEndpoint(retrieve_discounts);
addEndpoint(update_discounts);
addEndpoint(list_discounts);
addEndpoint(delete_discounts);
addEndpoint(create_addons);
addEndpoint(retrieve_addons);
addEndpoint(update_addons);
addEndpoint(list_addons);
addEndpoint(update_images_addons);
addEndpoint(create_brands);
addEndpoint(retrieve_brands);
addEndpoint(update_brands);
addEndpoint(list_brands);
addEndpoint(update_images_brands);
addEndpoint(create_webhooks);
addEndpoint(retrieve_webhooks);
addEndpoint(update_webhooks);
addEndpoint(list_webhooks);
addEndpoint(delete_webhooks);
addEndpoint(retrieve_secret_webhooks);
addEndpoint(retrieve_webhooks_headers);
addEndpoint(update_webhooks_headers);
addEndpoint(retrieve_usage_events);
addEndpoint(list_usage_events);
addEndpoint(ingest_usage_events);
addEndpoint(create_meters);
addEndpoint(retrieve_meters);
addEndpoint(list_meters);
addEndpoint(archive_meters);
addEndpoint(unarchive_meters);

export type Filter = {
  type: 'resource' | 'operation' | 'tag' | 'tool';
  op: 'include' | 'exclude';
  value: string;
};

export function query(filters: Filter[], endpoints: Endpoint[]): Endpoint[] {
  const allExcludes = filters.length > 0 && filters.every((filter) => filter.op === 'exclude');
  const unmatchedFilters = new Set(filters);

  const filtered = endpoints.filter((endpoint: Endpoint) => {
    let included = false || allExcludes;

    for (const filter of filters) {
      if (match(filter, endpoint)) {
        unmatchedFilters.delete(filter);
        included = filter.op === 'include';
      }
    }

    return included;
  });

  // Check if any filters didn't match
  const unmatched = Array.from(unmatchedFilters).filter((f) => f.type === 'tool' || f.type === 'resource');
  if (unmatched.length > 0) {
    throw new Error(
      `The following filters did not match any endpoints: ${unmatched
        .map((f) => `${f.type}=${f.value}`)
        .join(', ')}`,
    );
  }

  return filtered;
}

function match({ type, value }: Filter, endpoint: Endpoint): boolean {
  switch (type) {
    case 'resource': {
      const regexStr = '^' + normalizeResource(value).replace(/\*/g, '.*') + '$';
      const regex = new RegExp(regexStr);
      return regex.test(normalizeResource(endpoint.metadata.resource));
    }
    case 'operation':
      return endpoint.metadata.operation === value;
    case 'tag':
      return endpoint.metadata.tags.includes(value);
    case 'tool':
      return endpoint.tool.name === value;
  }
}

function normalizeResource(resource: string): string {
  return resource.toLowerCase().replace(/[^a-z.*\-_]*/g, '');
}

```

--------------------------------------------------------------------------------
/MIGRATION.md:
--------------------------------------------------------------------------------

```markdown
# Migration guide

This guide outlines the changes and steps needed to migrate your codebase to the latest version of the Dodo Payments TypeScript SDK.

The main changes are that the SDK now relies on the [builtin Web fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) instead of `node-fetch` and has zero dependencies.

## Migration CLI

Most programs will only need minimal changes, but to assist there is a migration tool that will automatically update your code for the new version.
To use it, upgrade the `dodopayments` package, then run `./node_modules/.bin/dodopayments migrate ./your/src/folders` to update your code.
To preview the changes without writing them to disk, run the tool with `--dry`.

## Environment requirements

The minimum supported runtime and tooling versions are now:

- Node.js 20 LTS (Most recent non-EOL Node version)
- TypeScript 4.9
- Jest 28

## Breaking changes

### Web types for `withResponse`, `asResponse`, and `APIError.headers`

Because we now use the builtin Web fetch API on all platforms, if you wrote code that used `withResponse` or `asResponse` and then accessed `node-fetch`-specific properties on the result, you will need to switch to standardized alternatives.
For example, `body` is now a [Web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) rather than a [node `Readable`](https://nodejs.org/api/stream.html#readable-streams).

```ts
// Before:
const res = await client.example.retrieve('string/with/slash').asResponse();
res.body.pipe(process.stdout);

// After:
import { Readable } from 'node:stream';
const res = await client.example.retrieve('string/with/slash').asResponse();
Readable.fromWeb(res.body).pipe(process.stdout);
```

Additionally, the `headers` property on `APIError` objects is now an instance of the Web [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) class. It was previously defined as `Record<string, string | null | undefined>`.

### URI encoded path parameters

Path params are now properly encoded by default. If you were manually encoding path parameters before giving them to the SDK, you must now stop doing that and pass the
param without any encoding applied.

For example:

```diff
- client.example.retrieve(encodeURIComponent('string/with/slash'))
+ client.example.retrieve('string/with/slash') // retrieves /example/string%2Fwith%2Fslash
```

Previously without the `encodeURIComponent()` call we would have used the path `/example/string/with/slash`; now we'll use `/example/string%2Fwith%2Fslash`.

### Removed request options overloads

When making requests with no required body, query or header parameters, you must now explicitly pass `null`, `undefined` or an empty object `{}` to the params argument in order to customise request options.

```diff
client.example.list();
client.example.list({}, { headers: { ... } });
client.example.list(null, { headers: { ... } });
client.example.list(undefined, { headers: { ... } });
- client.example.list({ headers: { ... } });
+ client.example.list({}, { headers: { ... } });
```

This affects the following methods:

- `client.payments.list()`
- `client.subscriptions.list()`
- `client.subscriptions.retrieveUsageHistory()`
- `client.licenseKeys.list()`
- `client.licenseKeyInstances.list()`
- `client.customers.list()`
- `client.customers.customerPortal.create()`
- `client.customers.wallets.ledgerEntries.list()`
- `client.refunds.list()`
- `client.disputes.list()`
- `client.payouts.list()`
- `client.products.list()`
- `client.products.images.update()`
- `client.discounts.list()`
- `client.addons.list()`
- `client.webhooks.list()`
- `client.usageEvents.list()`
- `client.meters.list()`

### Removed `httpAgent` in favor of `fetchOptions`

The `httpAgent` client option has been removed in favor of a [platform-specific `fetchOptions` property](https://github.com/dodopayments/dodopayments-typescript#fetch-options).
This change was made as `httpAgent` relied on `node:http` agents which are not supported by any runtime's builtin fetch implementation.

If you were using `httpAgent` for proxy support, check out the [new proxy documentation](https://github.com/dodopayments/dodopayments-typescript#configuring-proxies).

Before:

```ts
import DodoPayments from 'dodopayments';
import http from 'http';
import { HttpsProxyAgent } from 'https-proxy-agent';

// Configure the default for all requests:
const client = new DodoPayments({
  httpAgent: new HttpsProxyAgent(process.env.PROXY_URL),
});
```

After:

```ts
import DodoPayments from 'dodopayments';
import * as undici from 'undici';

const proxyAgent = new undici.ProxyAgent(process.env.PROXY_URL);
const client = new DodoPayments({
  fetchOptions: {
    dispatcher: proxyAgent,
  },
});
```

### Changed exports

#### Refactor of `dodopayments/core`, `error`, `pagination`, `resource` and `uploads`

Much of the `dodopayments/core` file was intended to be internal-only but it was publicly accessible, as such it has been refactored and split up into internal and public files, with public-facing code moved to a new `core` folder and internal code moving to the private `internal` folder.

At the same time, we moved some public-facing files which were previously at the top level into `core` to make the file structure cleaner and more clear:

```typescript
// Before
import 'dodopayments/error';
import 'dodopayments/pagination';
import 'dodopayments/resource';
import 'dodopayments/uploads';

// After
import 'dodopayments/core/error';
import 'dodopayments/core/pagination';
import 'dodopayments/core/resource';
import 'dodopayments/core/uploads';
```

If you were relying on anything that was only exported from `dodopayments/core` and is also not accessible anywhere else, please open an issue and we'll consider adding it to the public API.

#### Resource classes

Previously under certain circumstances it was possible to import resource classes like `CheckoutSessions` directly from the root of the package. This was never valid at the type level and only worked in CommonJS files.
Now you must always either reference them as static class properties or import them directly from the files in which they are defined.

```typescript
// Before
const { CheckoutSessions } = require('dodopayments');

// After
const { DodoPayments } = require('dodopayments');
DodoPayments.CheckoutSessions; // or import directly from dodopayments/resources/checkout-sessions
```

#### Cleaned up `uploads` exports

As part of the `core` refactor, `dodopayments/uploads` was moved to `dodopayments/core/uploads`
and the following exports were removed, as they were not intended to be a part of the public API:

- `fileFromPath`
- `BlobPart`
- `BlobLike`
- `FileLike`
- `ResponseLike`
- `isResponseLike`
- `isBlobLike`
- `isFileLike`
- `isUploadable`
- `isMultipartBody`
- `maybeMultipartFormRequestOptions`
- `multipartFormRequestOptions`
- `createForm`

Note that `Uploadable` & `toFile` **are** still exported:

```typescript
import { type Uploadable, toFile } from 'dodopayments/core/uploads';
```

#### `APIClient`

The `APIClient` base client class has been removed as it is no longer needed. If you were importing this class then you must now import the main client class:

```typescript
// Before
import { APIClient } from 'dodopayments/core';

// After
import { DodoPayments } from 'dodopayments';
```

### File handling

The deprecated `fileFromPath` helper has been removed in favor of native Node.js streams:

```ts
// Before
DodoPayments.fileFromPath('path/to/file');

// After
import fs from 'fs';
fs.createReadStream('path/to/file');
```

Note that this function previously only worked on Node.js. If you're using Bun, you can use [`Bun.file`](https://bun.sh/docs/api/file-io) instead.

### Shims removal

Previously you could configure the types that the SDK used like this:

```ts
// Tell TypeScript and the package to use the global Web fetch instead of node-fetch.
import 'dodopayments/shims/web';
import DodoPayments from 'dodopayments';
```

The `dodopayments/shims` imports have been removed. Your global types must now be [correctly configured](#minimum-types-requirements).

### Pagination changes

The `for await` syntax **is not affected**. This still works as-is:

```ts
// Automatically fetches more pages as needed.
for await (const paymentListResponse of client.payments.list()) {
  console.log(paymentListResponse);
}
```

The interface for manually paginating through list results has been simplified:

```ts
// Before
page.nextPageParams();
page.nextPageInfo();
// Required manually handling { url } | { params } type

// After
page.nextPageRequestOptions();
```

#### Removed unnecessary classes

Page classes for individual methods are now type aliases:

```ts
// Before
export class PaymentListResponsesDefaultPageNumberPagination extends DefaultPageNumberPagination<PaymentListResponse> {}

// After
export type PaymentListResponsesDefaultPageNumberPagination =
  DefaultPageNumberPagination<PaymentListResponse>;
```

If you were importing these classes at runtime, you'll need to switch to importing the base class or only import them at the type-level.

### `dodopayments/src` directory removed

Previously IDEs may have auto-completed imports from the `dodopayments/src` directory, however this
directory was only included for an improved go-to-definition experience and should not have been used at runtime.

If you have any `dodopayments/src/*` imports, you will need to replace them with `dodopayments/*`.

```ts
// Before
import DodoPayments from 'dodopayments/src';

// After
import DodoPayments from 'dodopayments';
```

## TypeScript troubleshooting

When referencing the library after updating, you may encounter new type errors related to JS features like private properties or fetch classes like Request, Response, and Headers.
To resolve these issues, configure your tsconfig.json and install the appropriate `@types` packages for your runtime environment using the guidelines below:

### Browsers

`tsconfig.json`

```jsonc
{
  "target": "ES2018", // note: we recommend ES2020 or higher
  "lib": ["DOM", "DOM.Iterable", "ES2018"]
}
```

### Node.js

`tsconfig.json`

```jsonc
{
  "target": "ES2018" // note: we recommend ES2020 or higher
}
```

`package.json`

```json
{
  "devDependencies": {
    "@types/node": ">= 20"
  }
}
```

### Cloudflare Workers

`tsconfig.json`

```jsonc
{
  "target": "ES2018", // note: we recommend ES2020 or higher
  "lib": ["ES2020"], // <- needed by @cloudflare/workers-types
  "types": ["@cloudflare/workers-types"]
}
```

`package.json`

```json
{
  "devDependencies": {
    "@cloudflare/workers-types": ">= 0.20221111.0"
  }
}
```

### Bun

`tsconfig.json`

```jsonc
{
  "target": "ES2018" // note: we recommend ES2020 or higher
}
```

`package.json`

```json
{
  "devDependencies": {
    "@types/bun": ">= 1.2.0"
  }
}
```

```

--------------------------------------------------------------------------------
/src/resources/checkout-sessions.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { APIResource } from '../core/resource';
import * as MiscAPI from './misc';
import * as PaymentsAPI from './payments';
import * as SubscriptionsAPI from './subscriptions';
import { APIPromise } from '../core/api-promise';
import { RequestOptions } from '../internal/request-options';
import { path } from '../internal/utils/path';

export class CheckoutSessions extends APIResource {
  create(body: CheckoutSessionCreateParams, options?: RequestOptions): APIPromise<CheckoutSessionResponse> {
    return this._client.post('/checkouts', { body, ...options });
  }

  retrieve(id: string, options?: RequestOptions): APIPromise<CheckoutSessionStatus> {
    return this._client.get(path`/checkouts/${id}`, options);
  }
}

export interface CheckoutSessionRequest {
  product_cart: Array<CheckoutSessionRequest.ProductCart>;

  /**
   * Customers will never see payment methods that are not in this list. However,
   * adding a method here does not guarantee customers will see it. Availability
   * still depends on other factors (e.g., customer location, merchant settings).
   *
   * Disclaimar: Always provide 'credit' and 'debit' as a fallback. If all payment
   * methods are unavailable, checkout session will fail.
   */
  allowed_payment_method_types?: Array<PaymentsAPI.PaymentMethodTypes> | null;

  /**
   * Billing address information for the session
   */
  billing_address?: CheckoutSessionRequest.BillingAddress | null;

  /**
   * This field is ingored if adaptive pricing is disabled
   */
  billing_currency?: MiscAPI.Currency | null;

  /**
   * If confirm is true, all the details will be finalized. If required data is
   * missing, an API error is thrown.
   */
  confirm?: boolean;

  /**
   * Customer details for the session
   */
  customer?: PaymentsAPI.CustomerRequest | null;

  /**
   * Customization for the checkout session page
   */
  customization?: CheckoutSessionRequest.Customization;

  discount_code?: string | null;

  feature_flags?: CheckoutSessionRequest.FeatureFlags;

  /**
   * Override merchant default 3DS behaviour for this session
   */
  force_3ds?: boolean | null;

  /**
   * Additional metadata associated with the payment. Defaults to empty if not
   * provided.
   */
  metadata?: { [key: string]: string } | null;

  /**
   * The url to redirect after payment failure or success.
   */
  return_url?: string | null;

  /**
   * Display saved payment methods of a returning customer False by default
   */
  show_saved_payment_methods?: boolean;

  subscription_data?: CheckoutSessionRequest.SubscriptionData | null;
}

export namespace CheckoutSessionRequest {
  export interface ProductCart {
    /**
     * unique id of the product
     */
    product_id: string;

    quantity: number;

    /**
     * only valid if product is a subscription
     */
    addons?: Array<SubscriptionsAPI.AttachAddon> | null;

    /**
     * Amount the customer pays if pay_what_you_want is enabled. If disabled then
     * amount will be ignored Represented in the lowest denomination of the currency
     * (e.g., cents for USD). For example, to charge $1.00, pass `100`. Only applicable
     * for one time payments
     *
     * If amount is not set for pay_what_you_want product, customer is allowed to
     * select the amount.
     */
    amount?: number | null;
  }

  /**
   * Billing address information for the session
   */
  export interface BillingAddress {
    /**
     * Two-letter ISO country code (ISO 3166-1 alpha-2)
     */
    country: MiscAPI.CountryCode;

    /**
     * City name
     */
    city?: string | null;

    /**
     * State or province name
     */
    state?: string | null;

    /**
     * Street address including house number and unit/apartment if applicable
     */
    street?: string | null;

    /**
     * Postal code or ZIP code
     */
    zipcode?: string | null;
  }

  /**
   * Customization for the checkout session page
   */
  export interface Customization {
    /**
     * Force the checkout interface to render in a specific language (e.g. `en`, `es`)
     */
    force_language?: string | null;

    /**
     * Show on demand tag
     *
     * Default is true
     */
    show_on_demand_tag?: boolean;

    /**
     * Show order details by default
     *
     * Default is true
     */
    show_order_details?: boolean;

    /**
     * Theme of the page
     *
     * Default is `System`.
     */
    theme?: 'dark' | 'light' | 'system';
  }

  export interface FeatureFlags {
    /**
     * if customer is allowed to change currency, set it to true
     *
     * Default is true
     */
    allow_currency_selection?: boolean;

    /**
     * If the customer is allowed to apply discount code, set it to true.
     *
     * Default is true
     */
    allow_discount_code?: boolean;

    /**
     * If phone number is collected from customer, set it to rue
     *
     * Default is true
     */
    allow_phone_number_collection?: boolean;

    /**
     * If the customer is allowed to add tax id, set it to true
     *
     * Default is true
     */
    allow_tax_id?: boolean;

    /**
     * Set to true if a new customer object should be created. By default email is used
     * to find an existing customer to attach the session to
     *
     * Default is false
     */
    always_create_new_customer?: boolean;
  }

  export interface SubscriptionData {
    on_demand?: SubscriptionsAPI.OnDemandSubscription | null;

    /**
     * Optional trial period in days If specified, this value overrides the trial
     * period set in the product's price Must be between 0 and 10000 days
     */
    trial_period_days?: number | null;
  }
}

export interface CheckoutSessionResponse {
  /**
   * Checkout url
   */
  checkout_url: string;

  /**
   * The ID of the created checkout session
   */
  session_id: string;
}

export interface CheckoutSessionStatus {
  /**
   * Id of the checkout session
   */
  id: string;

  /**
   * Created at timestamp
   */
  created_at: string;

  /**
   * Customer email: prefers payment's customer, falls back to session
   */
  customer_email?: string | null;

  /**
   * Customer name: prefers payment's customer, falls back to session
   */
  customer_name?: string | null;

  /**
   * Id of the payment created by the checkout sessions.
   *
   * Null if checkout sessions is still at the details collection stage.
   */
  payment_id?: string | null;

  /**
   * status of the payment.
   *
   * Null if checkout sessions is still at the details collection stage.
   */
  payment_status?: PaymentsAPI.IntentStatus | null;
}

export interface CheckoutSessionCreateParams {
  product_cart: Array<CheckoutSessionCreateParams.ProductCart>;

  /**
   * Customers will never see payment methods that are not in this list. However,
   * adding a method here does not guarantee customers will see it. Availability
   * still depends on other factors (e.g., customer location, merchant settings).
   *
   * Disclaimar: Always provide 'credit' and 'debit' as a fallback. If all payment
   * methods are unavailable, checkout session will fail.
   */
  allowed_payment_method_types?: Array<PaymentsAPI.PaymentMethodTypes> | null;

  /**
   * Billing address information for the session
   */
  billing_address?: CheckoutSessionCreateParams.BillingAddress | null;

  /**
   * This field is ingored if adaptive pricing is disabled
   */
  billing_currency?: MiscAPI.Currency | null;

  /**
   * If confirm is true, all the details will be finalized. If required data is
   * missing, an API error is thrown.
   */
  confirm?: boolean;

  /**
   * Customer details for the session
   */
  customer?: PaymentsAPI.CustomerRequest | null;

  /**
   * Customization for the checkout session page
   */
  customization?: CheckoutSessionCreateParams.Customization;

  discount_code?: string | null;

  feature_flags?: CheckoutSessionCreateParams.FeatureFlags;

  /**
   * Override merchant default 3DS behaviour for this session
   */
  force_3ds?: boolean | null;

  /**
   * Additional metadata associated with the payment. Defaults to empty if not
   * provided.
   */
  metadata?: { [key: string]: string } | null;

  /**
   * The url to redirect after payment failure or success.
   */
  return_url?: string | null;

  /**
   * Display saved payment methods of a returning customer False by default
   */
  show_saved_payment_methods?: boolean;

  subscription_data?: CheckoutSessionCreateParams.SubscriptionData | null;
}

export namespace CheckoutSessionCreateParams {
  export interface ProductCart {
    /**
     * unique id of the product
     */
    product_id: string;

    quantity: number;

    /**
     * only valid if product is a subscription
     */
    addons?: Array<SubscriptionsAPI.AttachAddon> | null;

    /**
     * Amount the customer pays if pay_what_you_want is enabled. If disabled then
     * amount will be ignored Represented in the lowest denomination of the currency
     * (e.g., cents for USD). For example, to charge $1.00, pass `100`. Only applicable
     * for one time payments
     *
     * If amount is not set for pay_what_you_want product, customer is allowed to
     * select the amount.
     */
    amount?: number | null;
  }

  /**
   * Billing address information for the session
   */
  export interface BillingAddress {
    /**
     * Two-letter ISO country code (ISO 3166-1 alpha-2)
     */
    country: MiscAPI.CountryCode;

    /**
     * City name
     */
    city?: string | null;

    /**
     * State or province name
     */
    state?: string | null;

    /**
     * Street address including house number and unit/apartment if applicable
     */
    street?: string | null;

    /**
     * Postal code or ZIP code
     */
    zipcode?: string | null;
  }

  /**
   * Customization for the checkout session page
   */
  export interface Customization {
    /**
     * Force the checkout interface to render in a specific language (e.g. `en`, `es`)
     */
    force_language?: string | null;

    /**
     * Show on demand tag
     *
     * Default is true
     */
    show_on_demand_tag?: boolean;

    /**
     * Show order details by default
     *
     * Default is true
     */
    show_order_details?: boolean;

    /**
     * Theme of the page
     *
     * Default is `System`.
     */
    theme?: 'dark' | 'light' | 'system';
  }

  export interface FeatureFlags {
    /**
     * if customer is allowed to change currency, set it to true
     *
     * Default is true
     */
    allow_currency_selection?: boolean;

    /**
     * If the customer is allowed to apply discount code, set it to true.
     *
     * Default is true
     */
    allow_discount_code?: boolean;

    /**
     * If phone number is collected from customer, set it to rue
     *
     * Default is true
     */
    allow_phone_number_collection?: boolean;

    /**
     * If the customer is allowed to add tax id, set it to true
     *
     * Default is true
     */
    allow_tax_id?: boolean;

    /**
     * Set to true if a new customer object should be created. By default email is used
     * to find an existing customer to attach the session to
     *
     * Default is false
     */
    always_create_new_customer?: boolean;
  }

  export interface SubscriptionData {
    on_demand?: SubscriptionsAPI.OnDemandSubscription | null;

    /**
     * Optional trial period in days If specified, this value overrides the trial
     * period set in the product's price Must be between 0 and 10000 days
     */
    trial_period_days?: number | null;
  }
}

export declare namespace CheckoutSessions {
  export {
    type CheckoutSessionRequest as CheckoutSessionRequest,
    type CheckoutSessionResponse as CheckoutSessionResponse,
    type CheckoutSessionStatus as CheckoutSessionStatus,
    type CheckoutSessionCreateParams as CheckoutSessionCreateParams,
  };
}

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/payments/create-payments.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import DodoPayments from 'dodopayments';

export const metadata: Metadata = {
  resource: 'payments',
  operation: 'write',
  tags: [],
  httpMethod: 'post',
  httpPath: '/payments',
  operationId: 'create_one_time_payment_handler',
};

export const tool: Tool = {
  name: 'create_payments',
  description: '',
  inputSchema: {
    type: 'object',
    properties: {
      billing: {
        $ref: '#/$defs/billing_address',
      },
      customer: {
        $ref: '#/$defs/customer_request',
      },
      product_cart: {
        type: 'array',
        description: 'List of products in the cart. Must contain at least 1 and at most 100 items.',
        items: {
          $ref: '#/$defs/one_time_product_cart_item',
        },
      },
      allowed_payment_method_types: {
        type: 'array',
        description:
          'List of payment methods allowed during checkout.\n\nCustomers will **never** see payment methods that are **not** in this list.\nHowever, adding a method here **does not guarantee** customers will see it.\nAvailability still depends on other factors (e.g., customer location, merchant settings).',
        items: {
          $ref: '#/$defs/payment_method_types',
        },
      },
      billing_currency: {
        $ref: '#/$defs/currency',
      },
      discount_code: {
        type: 'string',
        description: 'Discount Code to apply to the transaction',
      },
      force_3ds: {
        type: 'boolean',
        description: 'Override merchant default 3DS behaviour for this payment',
      },
      metadata: {
        type: 'object',
        description: 'Additional metadata associated with the payment.\nDefaults to empty if not provided.',
        additionalProperties: true,
      },
      payment_link: {
        type: 'boolean',
        description: 'Whether to generate a payment link. Defaults to false if not specified.',
      },
      return_url: {
        type: 'string',
        description: 'Optional URL to redirect the customer after payment.\nMust be a valid URL if provided.',
      },
      show_saved_payment_methods: {
        type: 'boolean',
        description: 'Display saved payment methods of a returning customer\nFalse by default',
      },
      tax_id: {
        type: 'string',
        description:
          'Tax ID in case the payment is B2B. If tax id validation fails the payment creation will fail',
      },
    },
    required: ['billing', 'customer', 'product_cart'],
    $defs: {
      billing_address: {
        type: 'object',
        properties: {
          city: {
            type: 'string',
            description: 'City name',
          },
          country: {
            $ref: '#/$defs/country_code',
          },
          state: {
            type: 'string',
            description: 'State or province name',
          },
          street: {
            type: 'string',
            description: 'Street address including house number and unit/apartment if applicable',
          },
          zipcode: {
            type: 'string',
            description: 'Postal code or ZIP code',
          },
        },
        required: ['city', 'country', 'state', 'street', 'zipcode'],
      },
      country_code: {
        type: 'string',
        description: 'ISO country code alpha2 variant',
        enum: [
          'AF',
          'AX',
          'AL',
          'DZ',
          'AS',
          'AD',
          'AO',
          'AI',
          'AQ',
          'AG',
          'AR',
          'AM',
          'AW',
          'AU',
          'AT',
          'AZ',
          'BS',
          'BH',
          'BD',
          'BB',
          'BY',
          'BE',
          'BZ',
          'BJ',
          'BM',
          'BT',
          'BO',
          'BQ',
          'BA',
          'BW',
          'BV',
          'BR',
          'IO',
          'BN',
          'BG',
          'BF',
          'BI',
          'KH',
          'CM',
          'CA',
          'CV',
          'KY',
          'CF',
          'TD',
          'CL',
          'CN',
          'CX',
          'CC',
          'CO',
          'KM',
          'CG',
          'CD',
          'CK',
          'CR',
          'CI',
          'HR',
          'CU',
          'CW',
          'CY',
          'CZ',
          'DK',
          'DJ',
          'DM',
          'DO',
          'EC',
          'EG',
          'SV',
          'GQ',
          'ER',
          'EE',
          'ET',
          'FK',
          'FO',
          'FJ',
          'FI',
          'FR',
          'GF',
          'PF',
          'TF',
          'GA',
          'GM',
          'GE',
          'DE',
          'GH',
          'GI',
          'GR',
          'GL',
          'GD',
          'GP',
          'GU',
          'GT',
          'GG',
          'GN',
          'GW',
          'GY',
          'HT',
          'HM',
          'VA',
          'HN',
          'HK',
          'HU',
          'IS',
          'IN',
          'ID',
          'IR',
          'IQ',
          'IE',
          'IM',
          'IL',
          'IT',
          'JM',
          'JP',
          'JE',
          'JO',
          'KZ',
          'KE',
          'KI',
          'KP',
          'KR',
          'KW',
          'KG',
          'LA',
          'LV',
          'LB',
          'LS',
          'LR',
          'LY',
          'LI',
          'LT',
          'LU',
          'MO',
          'MK',
          'MG',
          'MW',
          'MY',
          'MV',
          'ML',
          'MT',
          'MH',
          'MQ',
          'MR',
          'MU',
          'YT',
          'MX',
          'FM',
          'MD',
          'MC',
          'MN',
          'ME',
          'MS',
          'MA',
          'MZ',
          'MM',
          'NA',
          'NR',
          'NP',
          'NL',
          'NC',
          'NZ',
          'NI',
          'NE',
          'NG',
          'NU',
          'NF',
          'MP',
          'NO',
          'OM',
          'PK',
          'PW',
          'PS',
          'PA',
          'PG',
          'PY',
          'PE',
          'PH',
          'PN',
          'PL',
          'PT',
          'PR',
          'QA',
          'RE',
          'RO',
          'RU',
          'RW',
          'BL',
          'SH',
          'KN',
          'LC',
          'MF',
          'PM',
          'VC',
          'WS',
          'SM',
          'ST',
          'SA',
          'SN',
          'RS',
          'SC',
          'SL',
          'SG',
          'SX',
          'SK',
          'SI',
          'SB',
          'SO',
          'ZA',
          'GS',
          'SS',
          'ES',
          'LK',
          'SD',
          'SR',
          'SJ',
          'SZ',
          'SE',
          'CH',
          'SY',
          'TW',
          'TJ',
          'TZ',
          'TH',
          'TL',
          'TG',
          'TK',
          'TO',
          'TT',
          'TN',
          'TR',
          'TM',
          'TC',
          'TV',
          'UG',
          'UA',
          'AE',
          'GB',
          'UM',
          'US',
          'UY',
          'UZ',
          'VU',
          'VE',
          'VN',
          'VG',
          'VI',
          'WF',
          'EH',
          'YE',
          'ZM',
          'ZW',
        ],
      },
      customer_request: {
        anyOf: [
          {
            $ref: '#/$defs/attach_existing_customer',
          },
          {
            $ref: '#/$defs/new_customer',
          },
        ],
        title: 'Customer Request',
      },
      attach_existing_customer: {
        type: 'object',
        title: 'Attach Existing Customer',
        properties: {
          customer_id: {
            type: 'string',
          },
        },
        required: ['customer_id'],
      },
      new_customer: {
        type: 'object',
        title: 'New Customer',
        properties: {
          email: {
            type: 'string',
            description: 'Email is required for creating a new customer',
          },
          name: {
            type: 'string',
            description:
              'Optional full name of the customer. If provided during session creation,\nit is persisted and becomes immutable for the session. If omitted here,\nit can be provided later via the confirm API.',
          },
          phone_number: {
            type: 'string',
          },
        },
        required: ['email'],
      },
      one_time_product_cart_item: {
        type: 'object',
        title: 'One-Time Product Cart Item',
        properties: {
          product_id: {
            type: 'string',
          },
          quantity: {
            type: 'integer',
          },
          amount: {
            type: 'integer',
            description:
              'Amount the customer pays if pay_what_you_want is enabled. If disabled then amount will be ignored\nRepresented in the lowest denomination of the currency (e.g., cents for USD).\nFor example, to charge $1.00, pass `100`.',
          },
        },
        required: ['product_id', 'quantity'],
      },
      payment_method_types: {
        type: 'string',
        enum: [
          'credit',
          'debit',
          'upi_collect',
          'upi_intent',
          'apple_pay',
          'cashapp',
          'google_pay',
          'multibanco',
          'bancontact_card',
          'eps',
          'ideal',
          'przelewy24',
          'affirm',
          'klarna',
          'sepa',
          'ach',
          'amazon_pay',
          'afterpay_clearpay',
        ],
      },
      currency: {
        type: 'string',
        enum: [
          'AED',
          'ALL',
          'AMD',
          'ANG',
          'AOA',
          'ARS',
          'AUD',
          'AWG',
          'AZN',
          'BAM',
          'BBD',
          'BDT',
          'BGN',
          'BHD',
          'BIF',
          'BMD',
          'BND',
          'BOB',
          'BRL',
          'BSD',
          'BWP',
          'BYN',
          'BZD',
          'CAD',
          'CHF',
          'CLP',
          'CNY',
          'COP',
          'CRC',
          'CUP',
          'CVE',
          'CZK',
          'DJF',
          'DKK',
          'DOP',
          'DZD',
          'EGP',
          'ETB',
          'EUR',
          'FJD',
          'FKP',
          'GBP',
          'GEL',
          'GHS',
          'GIP',
          'GMD',
          'GNF',
          'GTQ',
          'GYD',
          'HKD',
          'HNL',
          'HRK',
          'HTG',
          'HUF',
          'IDR',
          'ILS',
          'INR',
          'IQD',
          'JMD',
          'JOD',
          'JPY',
          'KES',
          'KGS',
          'KHR',
          'KMF',
          'KRW',
          'KWD',
          'KYD',
          'KZT',
          'LAK',
          'LBP',
          'LKR',
          'LRD',
          'LSL',
          'LYD',
          'MAD',
          'MDL',
          'MGA',
          'MKD',
          'MMK',
          'MNT',
          'MOP',
          'MRU',
          'MUR',
          'MVR',
          'MWK',
          'MXN',
          'MYR',
          'MZN',
          'NAD',
          'NGN',
          'NIO',
          'NOK',
          'NPR',
          'NZD',
          'OMR',
          'PAB',
          'PEN',
          'PGK',
          'PHP',
          'PKR',
          'PLN',
          'PYG',
          'QAR',
          'RON',
          'RSD',
          'RUB',
          'RWF',
          'SAR',
          'SBD',
          'SCR',
          'SEK',
          'SGD',
          'SHP',
          'SLE',
          'SLL',
          'SOS',
          'SRD',
          'SSP',
          'STN',
          'SVC',
          'SZL',
          'THB',
          'TND',
          'TOP',
          'TRY',
          'TTD',
          'TWD',
          'TZS',
          'UAH',
          'UGX',
          'USD',
          'UYU',
          'UZS',
          'VES',
          'VND',
          'VUV',
          'WST',
          'XAF',
          'XCD',
          'XOF',
          'XPF',
          'YER',
          'ZAR',
          'ZMW',
        ],
      },
    },
  },
  annotations: {},
};

export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
  const body = args as any;
  return asTextContentResult(await client.payments.create(body));
};

export default { metadata, tool, handler };

```

--------------------------------------------------------------------------------
/packages/mcp-server/src/compat.ts:
--------------------------------------------------------------------------------

```typescript
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import { Endpoint } from './tools';

export interface ClientCapabilities {
  topLevelUnions: boolean;
  validJson: boolean;
  refs: boolean;
  unions: boolean;
  formats: boolean;
  toolNameLength: number | undefined;
}

export const defaultClientCapabilities: ClientCapabilities = {
  topLevelUnions: true,
  validJson: true,
  refs: true,
  unions: true,
  formats: true,
  toolNameLength: undefined,
};

export const ClientType = z.enum(['openai-agents', 'claude', 'claude-code', 'cursor', 'infer']);
export type ClientType = z.infer<typeof ClientType>;

// Client presets for compatibility
// Note that these could change over time as models get better, so this is
// a best effort.
export const knownClients: Record<Exclude<ClientType, 'infer'>, ClientCapabilities> = {
  'openai-agents': {
    topLevelUnions: false,
    validJson: true,
    refs: true,
    unions: true,
    formats: true,
    toolNameLength: undefined,
  },
  claude: {
    topLevelUnions: true,
    validJson: false,
    refs: true,
    unions: true,
    formats: true,
    toolNameLength: undefined,
  },
  'claude-code': {
    topLevelUnions: false,
    validJson: true,
    refs: true,
    unions: true,
    formats: true,
    toolNameLength: undefined,
  },
  cursor: {
    topLevelUnions: false,
    validJson: true,
    refs: false,
    unions: false,
    formats: false,
    toolNameLength: 50,
  },
};

/**
 * Attempts to parse strings into JSON objects
 */
export function parseEmbeddedJSON(args: Record<string, unknown>, schema: Record<string, unknown>) {
  let updated = false;
  const newArgs: Record<string, unknown> = Object.assign({}, args);

  for (const [key, value] of Object.entries(newArgs)) {
    if (typeof value === 'string') {
      try {
        const parsed = JSON.parse(value);
        // Only parse if result is a plain object (not array, null, or primitive)
        if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
          newArgs[key] = parsed;
          updated = true;
        }
      } catch (e) {
        // Not valid JSON, leave as is
      }
    }
  }

  if (updated) {
    return newArgs;
  }

  return args;
}

export type JSONSchema = {
  type?: string;
  properties?: Record<string, JSONSchema>;
  required?: string[];
  anyOf?: JSONSchema[];
  $ref?: string;
  $defs?: Record<string, JSONSchema>;
  [key: string]: any;
};

/**
 * Truncates tool names to the specified length while ensuring uniqueness.
 * If truncation would cause duplicate names, appends a number to make them unique.
 */
export function truncateToolNames(names: string[], maxLength: number): Map<string, string> {
  if (maxLength <= 0) {
    return new Map();
  }

  const renameMap = new Map<string, string>();
  const usedNames = new Set<string>();

  const toTruncate = names.filter((name) => name.length > maxLength);

  if (toTruncate.length === 0) {
    return renameMap;
  }

  const willCollide =
    new Set(toTruncate.map((name) => name.slice(0, maxLength - 1))).size < toTruncate.length;

  if (!willCollide) {
    for (const name of toTruncate) {
      const truncatedName = name.slice(0, maxLength);
      renameMap.set(name, truncatedName);
    }
  } else {
    const baseLength = maxLength - 1;

    for (const name of toTruncate) {
      const baseName = name.slice(0, baseLength);
      let counter = 1;

      while (usedNames.has(baseName + counter)) {
        counter++;
      }

      const finalName = baseName + counter;
      renameMap.set(name, finalName);
      usedNames.add(finalName);
    }
  }

  return renameMap;
}

/**
 * Removes top-level unions from a tool by splitting it into multiple tools,
 * one for each variant in the union.
 */
export function removeTopLevelUnions(tool: Tool): Tool[] {
  const inputSchema = tool.inputSchema as JSONSchema;
  const variants = inputSchema.anyOf;

  if (!variants || !Array.isArray(variants) || variants.length === 0) {
    return [tool];
  }

  const defs = inputSchema.$defs || {};

  return variants.map((variant, index) => {
    const variantSchema: JSONSchema = {
      ...inputSchema,
      ...variant,
      type: 'object',
      properties: {
        ...(inputSchema.properties || {}),
        ...(variant.properties || {}),
      },
    };

    delete variantSchema.anyOf;

    if (!variantSchema['description']) {
      variantSchema['description'] = tool.description;
    }

    const usedDefs = findUsedDefs(variant, defs);
    if (Object.keys(usedDefs).length > 0) {
      variantSchema.$defs = usedDefs;
    } else {
      delete variantSchema.$defs;
    }

    return {
      ...tool,
      name: `${tool.name}_${toSnakeCase(variant['title'] || `variant${index + 1}`)}`,
      description: variant['description'] || tool.description,
      inputSchema: variantSchema,
    } as Tool;
  });
}

function findUsedDefs(
  schema: JSONSchema,
  defs: Record<string, JSONSchema>,
  visited: Set<string> = new Set(),
): Record<string, JSONSchema> {
  const usedDefs: Record<string, JSONSchema> = {};

  if (typeof schema !== 'object' || schema === null) {
    return usedDefs;
  }

  if (schema.$ref) {
    const refParts = schema.$ref.split('/');
    if (refParts[0] === '#' && refParts[1] === '$defs' && refParts[2]) {
      const defName = refParts[2];
      const def = defs[defName];
      if (def && !visited.has(schema.$ref)) {
        usedDefs[defName] = def;
        visited.add(schema.$ref);
        Object.assign(usedDefs, findUsedDefs(def, defs, visited));
        visited.delete(schema.$ref);
      }
    }
    return usedDefs;
  }

  for (const key in schema) {
    if (key !== '$defs' && typeof schema[key] === 'object' && schema[key] !== null) {
      Object.assign(usedDefs, findUsedDefs(schema[key] as JSONSchema, defs, visited));
    }
  }

  return usedDefs;
}

// Export for testing
export { findUsedDefs };

/**
 * Inlines all $refs in a schema, eliminating $defs.
 * If a circular reference is detected, the circular property is removed.
 */
export function inlineRefs(schema: JSONSchema): JSONSchema {
  if (!schema || typeof schema !== 'object') {
    return schema;
  }

  const clonedSchema = { ...schema };
  const defs: Record<string, JSONSchema> = schema.$defs || {};

  delete clonedSchema.$defs;

  const result = inlineRefsRecursive(clonedSchema, defs, new Set<string>());
  // The top level can never be null
  return result === null ? {} : result;
}

function inlineRefsRecursive(
  schema: JSONSchema,
  defs: Record<string, JSONSchema>,
  refPath: Set<string>,
): JSONSchema | null {
  if (!schema || typeof schema !== 'object') {
    return schema;
  }

  if (Array.isArray(schema)) {
    return schema.map((item) => {
      const processed = inlineRefsRecursive(item, defs, refPath);
      return processed === null ? {} : processed;
    }) as JSONSchema;
  }

  const result = { ...schema };

  if ('$ref' in result && typeof result.$ref === 'string') {
    if (result.$ref.startsWith('#/$defs/')) {
      const refName = result.$ref.split('/').pop() as string;
      const def = defs[refName];

      // If we've already seen this ref in our path, we have a circular reference
      if (refPath.has(result.$ref)) {
        // For circular references, we completely remove the property
        // by returning null. The parent will remove it.
        return null;
      }

      if (def) {
        const newRefPath = new Set(refPath);
        newRefPath.add(result.$ref);

        const inlinedDef = inlineRefsRecursive({ ...def }, defs, newRefPath);

        if (inlinedDef === null) {
          return { ...result };
        }

        // Merge the inlined definition with the original schema's properties
        // but preserve things like description, etc.
        const { $ref, ...rest } = result;
        return { ...inlinedDef, ...rest };
      }
    }

    // Keep external refs as-is
    return result;
  }

  for (const key in result) {
    if (result[key] && typeof result[key] === 'object') {
      const processed = inlineRefsRecursive(result[key] as JSONSchema, defs, refPath);
      if (processed === null) {
        // Remove properties that would cause circular references
        delete result[key];
      } else {
        result[key] = processed;
      }
    }
  }

  return result;
}

/**
 * Removes anyOf fields from a schema, using only the first variant.
 */
export function removeAnyOf(schema: JSONSchema): JSONSchema {
  if (!schema || typeof schema !== 'object') {
    return schema;
  }

  if (Array.isArray(schema)) {
    return schema.map((item) => removeAnyOf(item)) as JSONSchema;
  }

  const result = { ...schema };

  if ('anyOf' in result && Array.isArray(result.anyOf) && result.anyOf.length > 0) {
    const firstVariant = result.anyOf[0];

    if (firstVariant && typeof firstVariant === 'object') {
      // Special handling for properties to ensure deep merge
      if (firstVariant.properties && result.properties) {
        result.properties = {
          ...result.properties,
          ...(firstVariant.properties as Record<string, JSONSchema>),
        };
      } else if (firstVariant.properties) {
        result.properties = { ...firstVariant.properties };
      }

      for (const key in firstVariant) {
        if (key !== 'properties') {
          result[key] = firstVariant[key];
        }
      }
    }

    delete result.anyOf;
  }

  for (const key in result) {
    if (result[key] && typeof result[key] === 'object') {
      result[key] = removeAnyOf(result[key] as JSONSchema);
    }
  }

  return result;
}

/**
 * Removes format fields from a schema and appends them to the description.
 */
export function removeFormats(schema: JSONSchema, formatsCapability: boolean): JSONSchema {
  if (formatsCapability) {
    return schema;
  }

  if (!schema || typeof schema !== 'object') {
    return schema;
  }

  if (Array.isArray(schema)) {
    return schema.map((item) => removeFormats(item, formatsCapability)) as JSONSchema;
  }

  const result = { ...schema };

  if ('format' in result && typeof result['format'] === 'string') {
    const formatStr = `(format: "${result['format']}")`;

    if ('description' in result && typeof result['description'] === 'string') {
      result['description'] = `${result['description']} ${formatStr}`;
    } else {
      result['description'] = formatStr;
    }

    delete result['format'];
  }

  for (const key in result) {
    if (result[key] && typeof result[key] === 'object') {
      result[key] = removeFormats(result[key] as JSONSchema, formatsCapability);
    }
  }

  return result;
}

/**
 * Applies all compatibility transformations to the endpoints based on the provided capabilities.
 */
export function applyCompatibilityTransformations(
  endpoints: Endpoint[],
  capabilities: ClientCapabilities,
): Endpoint[] {
  let transformedEndpoints = [...endpoints];

  // Handle top-level unions first as this changes tool names
  if (!capabilities.topLevelUnions) {
    const newEndpoints: Endpoint[] = [];

    for (const endpoint of transformedEndpoints) {
      const variantTools = removeTopLevelUnions(endpoint.tool);

      if (variantTools.length === 1) {
        newEndpoints.push(endpoint);
      } else {
        for (const variantTool of variantTools) {
          newEndpoints.push({
            ...endpoint,
            tool: variantTool,
          });
        }
      }
    }

    transformedEndpoints = newEndpoints;
  }

  if (capabilities.toolNameLength) {
    const toolNames = transformedEndpoints.map((endpoint) => endpoint.tool.name);
    const renameMap = truncateToolNames(toolNames, capabilities.toolNameLength);

    transformedEndpoints = transformedEndpoints.map((endpoint) => ({
      ...endpoint,
      tool: {
        ...endpoint.tool,
        name: renameMap.get(endpoint.tool.name) ?? endpoint.tool.name,
      },
    }));
  }

  if (!capabilities.refs || !capabilities.unions || !capabilities.formats) {
    transformedEndpoints = transformedEndpoints.map((endpoint) => {
      let schema = endpoint.tool.inputSchema as JSONSchema;

      if (!capabilities.refs) {
        schema = inlineRefs(schema);
      }

      if (!capabilities.unions) {
        schema = removeAnyOf(schema);
      }

      if (!capabilities.formats) {
        schema = removeFormats(schema, capabilities.formats);
      }

      return {
        ...endpoint,
        tool: {
          ...endpoint.tool,
          inputSchema: schema as typeof endpoint.tool.inputSchema,
        },
      };
    });
  }

  return transformedEndpoints;
}

function toSnakeCase(str: string): string {
  return str
    .replace(/\s+/g, '_')
    .replace(/([a-z])([A-Z])/g, '$1_$2')
    .toLowerCase();
}

```

--------------------------------------------------------------------------------
/src/resources/payments.ts:
--------------------------------------------------------------------------------

```typescript
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

import { APIResource } from '../core/resource';
import * as DisputesAPI from './disputes';
import * as MiscAPI from './misc';
import * as RefundsAPI from './refunds';
import { APIPromise } from '../core/api-promise';
import {
  DefaultPageNumberPagination,
  type DefaultPageNumberPaginationParams,
  PagePromise,
} from '../core/pagination';
import { RequestOptions } from '../internal/request-options';
import { path } from '../internal/utils/path';

export class Payments extends APIResource {
  create(body: PaymentCreateParams, options?: RequestOptions): APIPromise<PaymentCreateResponse> {
    return this._client.post('/payments', { body, ...options });
  }

  retrieve(paymentID: string, options?: RequestOptions): APIPromise<Payment> {
    return this._client.get(path`/payments/${paymentID}`, options);
  }

  list(
    query: PaymentListParams | null | undefined = {},
    options?: RequestOptions,
  ): PagePromise<PaymentListResponsesDefaultPageNumberPagination, PaymentListResponse> {
    return this._client.getAPIList('/payments', DefaultPageNumberPagination<PaymentListResponse>, {
      query,
      ...options,
    });
  }

  retrieveLineItems(
    paymentID: string,
    options?: RequestOptions,
  ): APIPromise<PaymentRetrieveLineItemsResponse> {
    return this._client.get(path`/payments/${paymentID}/line-items`, options);
  }
}

export type PaymentListResponsesDefaultPageNumberPagination =
  DefaultPageNumberPagination<PaymentListResponse>;

export interface AttachExistingCustomer {
  customer_id: string;
}

export interface BillingAddress {
  /**
   * City name
   */
  city: string;

  /**
   * Two-letter ISO country code (ISO 3166-1 alpha-2)
   */
  country: MiscAPI.CountryCode;

  /**
   * State or province name
   */
  state: string;

  /**
   * Street address including house number and unit/apartment if applicable
   */
  street: string;

  /**
   * Postal code or ZIP code
   */
  zipcode: string;
}

export interface CreateNewCustomer {
  email: string;

  name: string;

  /**
   * When false, the most recently created customer object with the given email is
   * used if exists. When true, a new customer object is always created False by
   * default
   */
  create_new_customer?: boolean;

  phone_number?: string | null;
}

export interface CustomerLimitedDetails {
  /**
   * Unique identifier for the customer
   */
  customer_id: string;

  /**
   * Email address of the customer
   */
  email: string;

  /**
   * Full name of the customer
   */
  name: string;

  /**
   * Phone number of the customer
   */
  phone_number?: string | null;
}

export type CustomerRequest = AttachExistingCustomer | NewCustomer;

export type IntentStatus =
  | 'succeeded'
  | 'failed'
  | 'cancelled'
  | 'processing'
  | 'requires_customer_action'
  | 'requires_merchant_action'
  | 'requires_payment_method'
  | 'requires_confirmation'
  | 'requires_capture'
  | 'partially_captured'
  | 'partially_captured_and_capturable';

export interface NewCustomer {
  /**
   * Email is required for creating a new customer
   */
  email: string;

  /**
   * Optional full name of the customer. If provided during session creation, it is
   * persisted and becomes immutable for the session. If omitted here, it can be
   * provided later via the confirm API.
   */
  name?: string | null;

  phone_number?: string | null;
}

export interface OneTimeProductCartItem {
  product_id: string;

  quantity: number;

  /**
   * Amount the customer pays if pay_what_you_want is enabled. If disabled then
   * amount will be ignored Represented in the lowest denomination of the currency
   * (e.g., cents for USD). For example, to charge $1.00, pass `100`.
   */
  amount?: number | null;
}

export interface Payment {
  /**
   * Billing address details for payments
   */
  billing: BillingAddress;

  /**
   * brand id this payment belongs to
   */
  brand_id: string;

  /**
   * Identifier of the business associated with the payment
   */
  business_id: string;

  /**
   * Timestamp when the payment was created
   */
  created_at: string;

  /**
   * Currency used for the payment
   */
  currency: MiscAPI.Currency;

  /**
   * Details about the customer who made the payment
   */
  customer: CustomerLimitedDetails;

  /**
   * brand id this payment belongs to
   */
  digital_products_delivered: boolean;

  /**
   * List of disputes associated with this payment
   */
  disputes: Array<DisputesAPI.Dispute>;

  /**
   * Additional custom data associated with the payment
   */
  metadata: { [key: string]: string };

  /**
   * Unique identifier for the payment
   */
  payment_id: string;

  /**
   * List of refunds issued for this payment
   */
  refunds: Array<Payment.Refund>;

  /**
   * The amount that will be credited to your Dodo balance after currency conversion
   * and processing. Especially relevant for adaptive pricing where the customer's
   * payment currency differs from your settlement currency.
   */
  settlement_amount: number;

  /**
   * The currency in which the settlement_amount will be credited to your Dodo
   * balance. This may differ from the customer's payment currency in adaptive
   * pricing scenarios.
   */
  settlement_currency: MiscAPI.Currency;

  /**
   * Total amount charged to the customer including tax, in smallest currency unit
   * (e.g. cents)
   */
  total_amount: number;

  /**
   * ISO2 country code of the card
   */
  card_issuing_country?: MiscAPI.CountryCode | null;

  /**
   * The last four digits of the card
   */
  card_last_four?: string | null;

  /**
   * Card network like VISA, MASTERCARD etc.
   */
  card_network?: string | null;

  /**
   * The type of card DEBIT or CREDIT
   */
  card_type?: string | null;

  /**
   * If payment is made using a checkout session, this field is set to the id of the
   * session.
   */
  checkout_session_id?: string | null;

  /**
   * The discount id if discount is applied
   */
  discount_id?: string | null;

  /**
   * An error code if the payment failed
   */
  error_code?: string | null;

  /**
   * An error message if the payment failed
   */
  error_message?: string | null;

  /**
   * Checkout URL
   */
  payment_link?: string | null;

  /**
   * Payment method used by customer (e.g. "card", "bank_transfer")
   */
  payment_method?: string | null;

  /**
   * Specific type of payment method (e.g. "visa", "mastercard")
   */
  payment_method_type?: string | null;

  /**
   * List of products purchased in a one-time payment
   */
  product_cart?: Array<Payment.ProductCart> | null;

  /**
   * This represents the portion of settlement_amount that corresponds to taxes
   * collected. Especially relevant for adaptive pricing where the tax component must
   * be tracked separately in your Dodo balance.
   */
  settlement_tax?: number | null;

  /**
   * Current status of the payment intent
   */
  status?: IntentStatus | null;

  /**
   * Identifier of the subscription if payment is part of a subscription
   */
  subscription_id?: string | null;

  /**
   * Amount of tax collected in smallest currency unit (e.g. cents)
   */
  tax?: number | null;

  /**
   * Timestamp when the payment was last updated
   */
  updated_at?: string | null;
}

export namespace Payment {
  export interface Refund {
    /**
     * The unique identifier of the business issuing the refund.
     */
    business_id: string;

    /**
     * The timestamp of when the refund was created in UTC.
     */
    created_at: string;

    /**
     * If true the refund is a partial refund
     */
    is_partial: boolean;

    /**
     * The unique identifier of the payment associated with the refund.
     */
    payment_id: string;

    /**
     * The unique identifier of the refund.
     */
    refund_id: string;

    /**
     * The current status of the refund.
     */
    status: RefundsAPI.RefundStatus;

    /**
     * The refunded amount.
     */
    amount?: number | null;

    /**
     * The currency of the refund, represented as an ISO 4217 currency code.
     */
    currency?: MiscAPI.Currency | null;

    /**
     * The reason provided for the refund, if any. Optional.
     */
    reason?: string | null;
  }

  export interface ProductCart {
    product_id: string;

    quantity: number;
  }
}

export type PaymentMethodTypes =
  | 'credit'
  | 'debit'
  | 'upi_collect'
  | 'upi_intent'
  | 'apple_pay'
  | 'cashapp'
  | 'google_pay'
  | 'multibanco'
  | 'bancontact_card'
  | 'eps'
  | 'ideal'
  | 'przelewy24'
  | 'affirm'
  | 'klarna'
  | 'sepa'
  | 'ach'
  | 'amazon_pay'
  | 'afterpay_clearpay';

export interface PaymentCreateResponse {
  /**
   * Client secret used to load Dodo checkout SDK NOTE : Dodo checkout SDK will be
   * coming soon
   */
  client_secret: string;

  /**
   * Limited details about the customer making the payment
   */
  customer: CustomerLimitedDetails;

  /**
   * Additional metadata associated with the payment
   */
  metadata: { [key: string]: string };

  /**
   * Unique identifier for the payment
   */
  payment_id: string;

  /**
   * Total amount of the payment in smallest currency unit (e.g. cents)
   */
  total_amount: number;

  /**
   * The discount id if discount is applied
   */
  discount_id?: string | null;

  /**
   * Expiry timestamp of the payment link
   */
  expires_on?: string | null;

  /**
   * Optional URL to a hosted payment page
   */
  payment_link?: string | null;

  /**
   * Optional list of products included in the payment
   */
  product_cart?: Array<OneTimeProductCartItem> | null;
}

export interface PaymentListResponse {
  brand_id: string;

  created_at: string;

  currency: MiscAPI.Currency;

  customer: CustomerLimitedDetails;

  digital_products_delivered: boolean;

  metadata: { [key: string]: string };

  payment_id: string;

  total_amount: number;

  payment_method?: string | null;

  payment_method_type?: string | null;

  status?: IntentStatus | null;

  subscription_id?: string | null;
}

export interface PaymentRetrieveLineItemsResponse {
  currency: MiscAPI.Currency;

  items: Array<PaymentRetrieveLineItemsResponse.Item>;
}

export namespace PaymentRetrieveLineItemsResponse {
  export interface Item {
    amount: number;

    items_id: string;

    refundable_amount: number;

    tax: number;

    description?: string | null;

    name?: string | null;
  }
}

export interface PaymentCreateParams {
  /**
   * Billing address details for the payment
   */
  billing: BillingAddress;

  /**
   * Customer information for the payment
   */
  customer: CustomerRequest;

  /**
   * List of products in the cart. Must contain at least 1 and at most 100 items.
   */
  product_cart: Array<OneTimeProductCartItem>;

  /**
   * List of payment methods allowed during checkout.
   *
   * Customers will **never** see payment methods that are **not** in this list.
   * However, adding a method here **does not guarantee** customers will see it.
   * Availability still depends on other factors (e.g., customer location, merchant
   * settings).
   */
  allowed_payment_method_types?: Array<PaymentMethodTypes> | null;

  /**
   * Fix the currency in which the end customer is billed. If Dodo Payments cannot
   * support that currency for this transaction, it will not proceed
   */
  billing_currency?: MiscAPI.Currency | null;

  /**
   * Discount Code to apply to the transaction
   */
  discount_code?: string | null;

  /**
   * Override merchant default 3DS behaviour for this payment
   */
  force_3ds?: boolean | null;

  /**
   * Additional metadata associated with the payment. Defaults to empty if not
   * provided.
   */
  metadata?: { [key: string]: string };

  /**
   * Whether to generate a payment link. Defaults to false if not specified.
   */
  payment_link?: boolean | null;

  /**
   * Optional URL to redirect the customer after payment. Must be a valid URL if
   * provided.
   */
  return_url?: string | null;

  /**
   * Display saved payment methods of a returning customer False by default
   */
  show_saved_payment_methods?: boolean;

  /**
   * Tax ID in case the payment is B2B. If tax id validation fails the payment
   * creation will fail
   */
  tax_id?: string | null;
}

export interface PaymentListParams extends DefaultPageNumberPaginationParams {
  /**
   * filter by Brand id
   */
  brand_id?: string;

  /**
   * Get events after this created time
   */
  created_at_gte?: string;

  /**
   * Get events created before this time
   */
  created_at_lte?: string;

  /**
   * Filter by customer id
   */
  customer_id?: string;

  /**
   * Filter by status
   */
  status?:
    | 'succeeded'
    | 'failed'
    | 'cancelled'
    | 'processing'
    | 'requires_customer_action'
    | 'requires_merchant_action'
    | 'requires_payment_method'
    | 'requires_confirmation'
    | 'requires_capture'
    | 'partially_captured'
    | 'partially_captured_and_capturable';

  /**
   * Filter by subscription id
   */
  subscription_id?: string;
}

export declare namespace Payments {
  export {
    type AttachExistingCustomer as AttachExistingCustomer,
    type BillingAddress as BillingAddress,
    type CreateNewCustomer as CreateNewCustomer,
    type CustomerLimitedDetails as CustomerLimitedDetails,
    type CustomerRequest as CustomerRequest,
    type IntentStatus as IntentStatus,
    type NewCustomer as NewCustomer,
    type OneTimeProductCartItem as OneTimeProductCartItem,
    type Payment as Payment,
    type PaymentMethodTypes as PaymentMethodTypes,
    type PaymentCreateResponse as PaymentCreateResponse,
    type PaymentListResponse as PaymentListResponse,
    type PaymentRetrieveLineItemsResponse as PaymentRetrieveLineItemsResponse,
    type PaymentListResponsesDefaultPageNumberPagination as PaymentListResponsesDefaultPageNumberPagination,
    type PaymentCreateParams as PaymentCreateParams,
    type PaymentListParams as PaymentListParams,
  };
}

```
Page 4/8FirstPrevNextLast