openapi: 3.0.0
info:
    version: '1.0'
    title: 'Qomon Transaction Bundles API'
    description: "This is Qomon's API definition for transaction bundles.\n\n## What is a Transaction Bundle?\n\nA **Transaction Bundle** is a container that groups together related financial operations for a contact. It represents a single payment or transaction event that may include multiple components:\n\n- **Transactions**: The core payment transactions (amounts, payment methods, dates, etc.)\n- **Memberships**: Membership subscriptions or renewals associated with the bundle\n- **Donations**: Donation contributions linked to the transaction\n- **Summary**: Automatically maintained counts and statistics about the bundle's contents\n\n### Bundle Structure\n\nA transaction bundle typically represents a single payment from a contact that can be split across different purposes:\n- A payment that includes both a membership fee and a donation\n- A membership renewal with additional contributions\n- Multiple transactions processed together\n\nThe bundle maintains a summary with up-to-date counts:\n- `transactions_count`: Number of transactions in the bundle\n- `memberships_count`: Number of memberships in the bundle\n- `donations_count`: Number of donations in the bundle\n\n### History Tracking\n\nEach bundle maintains a complete history of changes, allowing you to track:\n- When transactions, memberships, or donations were added or modified\n- Status changes for transactions\n- Creation and deletion events\n\n---\n\nTo get an authorization go to [Qomon's setting page](https://qomon.app/settings/extensions/connect) and create an API key."
servers:
    -
        description: 'Qomon production'
        url: 'https://incoming.qomon.app'
security:
    -
        apiKey: []
tags:
    -
        name: 'Transaction bundles'
        description: "Create, read, update, delete, and manage transaction bundles.\n        \n\nA bundle groups transactions, memberships, and donations together for a single payment event."
    -
        name: 'Settings and configuration'
        description: "Read-only reference data for transaction bundles.\n\nPayment methods, statuses, prices, and campaign codes configured for your group."
paths:
    /v1/transaction_bundles:
        post:
            tags: ['Transaction bundles']
            summary: 'Create a transaction_bundle'
            description: "Create a new transaction bundle. A bundle represents a single payment event and groups the financial transaction(s) with the products purchased (memberships, donations, or both).\n\n**One API key = one space**\nThe bundle is recorded in the space associated with the API key you use. The contact can belong to a different space — their profile will still show the transaction. This is the correct way to handle contacts who move between spaces or for national/world-level transactions: use the API key of the space where the transaction should appear financially.\n\n**Payer vs. Beneficiary**\n- `contact_id` in `transactions` → the **payer** (who made the payment)\n- `contact_id` in `memberships` → the **beneficiary** (who gets the membership)\n- `contact_id` in `donations` → the **beneficiary** (who gets the donation credit)\nThese can be different people. When they differ, the transaction will have the \"Tiers Payant\" status in most group configurations.\n\n**Getting valid IDs before creating**\n- Payment methods: `GET /v1/transaction_settings` → `payment_method_kinds`\n- Transaction statuses: `GET /v1/transaction_statuses` (use `default_status_id` from settings for new valid transactions)\n- Membership prices: `GET /v1/membership_prices`\n- Donation prices: `GET /v1/donation_prices`\n- Campaign codes: `GET /v1/code_campaigns`\n\n**`external_transaction_id`**\nMust be a plain integer. Used as your external reference and for deduplication. String suffixes (e.g. `\"1001_a\"`) are not supported.\n\n**Dates**\nAll dates must be valid ISO 8601 timestamps. The date you provide is stored as-is (UTC). Avoid invalid calendar dates (e.g. `2025-02-31`).\n\n**Required for creation:**\n- At least one transaction in `transactions` with: `amount`, `currency`, `payment_method_kind`, `contact_id`, `date`\n\n**Example — membership:**\n```json\n{\n  \"data\": {\n    \"transactions\": [\n      {\n        \"contact_id\": 123456,\n        \"amount\": 2000,\n        \"currency\": \"eur\",\n        \"payment_method_kind\": \"CB\",\n        \"date\": \"2026-03-29T12:59:52+02:00\",\n        \"external_transaction_id\": 1001,\n        \"status_id\": 1,\n        \"comment\": \"Imported via API\"\n      }\n    ],\n    \"memberships\": [\n      {\n        \"contact_id\": 123456,\n        \"membership_price_id\": 10,\n        \"start_date\": \"2026-01-01T00:00:00+02:00\",\n        \"end_date\": \"2026-12-31T00:00:00+02:00\",\n        \"amount\": 2000,\n        \"amount_initial\": 2000,\n        \"currency\": \"eur\"\n      }\n    ]\n  }\n}\n```\n> `status_id: 1` is illustrative — use the `default_status_id` from `GET /v1/transaction_settings` for your group.\n> `membership_price_id: 10` is illustrative — use IDs from `GET /v1/membership_prices`.\n\n**Example — donation:**\n```json\n{\n  \"data\": {\n    \"transactions\": [\n      {\n        \"contact_id\": 123456,\n        \"amount\": 5000,\n        \"currency\": \"eur\",\n        \"payment_method_kind\": \"CB\",\n        \"date\": \"2026-03-27T18:00:28+02:00\",\n        \"external_transaction_id\": 1002,\n        \"status_id\": 1,\n        \"comment\": \"Imported via API\"\n      }\n    ],\n    \"donations\": [\n      {\n        \"contact_id\": 123456,\n        \"amount\": 5000,\n        \"currency\": \"eur\",\n        \"donation_price_id\": 20\n      }\n    ]\n  }\n}\n```\n> `donation_price_id: 20` is illustrative — use IDs from `GET /v1/donation_prices`.\n\n**Example — different payer and beneficiary (Tiers Payant):**\n```json\n{\n  \"data\": {\n    \"transactions\": [\n      {\n        \"contact_id\": 123456,\n        \"amount\": 2000,\n        \"currency\": \"eur\",\n        \"payment_method_kind\": \"CB\",\n        \"date\": \"2026-03-29T10:00:00Z\",\n        \"external_transaction_id\": 1003,\n        \"status_id\": 1\n      }\n    ],\n    \"memberships\": [\n      {\n        \"contact_id\": 789012,\n        \"membership_price_id\": 10,\n        \"start_date\": \"2026-01-01T00:00:00Z\",\n        \"end_date\": \"2026-12-31T00:00:00Z\",\n        \"amount\": 2000,\n        \"amount_initial\": 2000,\n        \"currency\": \"eur\"\n      }\n    ]\n  }\n}\n```\nHere contact `123456` paid, but the membership is attributed to contact `789012`.\n"
            operationId: createTransactionBundle
            requestBody: {description: "Transaction bundle data. The bundle must contain at least one transaction.\n\n**Example:**\n```json\n{\n  \"data\": {\n    \"transactions\": [\n      {\n        \"amount\": 5000,\n        \"currency\": \"eur\",\n        \"payment_method_kind\": \"card\",\n        \"contact_id\": 123,\n        \"date\": \"2024-01-15T10:00:00Z\",\n        \"status_id\": 1\n      }\n    ],\n    \"memberships\": [\n      {\n        \"contact_id\": 123,\n        \"start_date\": \"2024-01-15T00:00:00Z\",\n        \"end_date\": \"2024-12-31T23:59:59Z\",\n        \"membership_price_id\": 5,\n        \"amount\": 3000,\n        \"amount_initial\": 3000,\n        \"currency\": \"eur\"\n      }\n    ],\n    \"donations\": [\n      {\n        \"contact_id\": 123,\n        \"amount\": 2000,\n        \"initial_amount\": 2000,\n        \"currency\": \"eur\",\n        \"donation_price_id\": 2\n      }\n    ]\n  }\n}\n```\n", required: true, content: {application/json: {schema: {type: object, required: [data], properties: {data: {type: object, description: "A transaction bundle groups together related financial operations (transactions, memberships, donations) that are part of a single payment event.\n\n**Structure:**\n- `transactions`: Array of payment transactions (required - at least one)\n- `memberships`: Array of membership subscriptions (optional)\n- `donations`: Array of donation contributions (optional)\n- `summary`: Automatically calculated counts (read-only, returned in responses only)\n\n**When Creating:**\n- Omit `id` field (auto-generated)\n- Omit `summary` (auto-calculated, returned in responses)\n- Include at least one transaction with required fields\n\n**When Updating:**\n- Include `id` field matching the bundle ID in the URL\n- Include `id` for existing items you want to modify\n- Omit `id` for new items you want to add\n- **Note:** Updates are additive - items not included in the request remain in the bundle unchanged (they are not removed)\n", properties: {id: {type: integer, description: 'Transaction bundle ID (required for updates, omitted for creation)'}, transactions: {type: array, description: 'Array of transactions in the bundle. At least one transaction is typically required.', items: {$ref: '#/components/schemas/Transaction'}}, memberships: {type: array, description: 'Array of memberships in the bundle. Optional - only include if the bundle contains memberships.', items: {$ref: '#/components/schemas/Membership'}}, donations: {type: array, description: 'Array of donations in the bundle. Optional - only include if the bundle contains donations.', items: {$ref: '#/components/schemas/Donation'}}, summary: {type: object, description: 'Summary object (read-only, automatically calculated)', additionalProperties: false, properties: {transactions_count: {type: integer}, memberships_count: {type: integer}, donations_count: {type: integer}}}}}}}}}}
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, description: "A transaction bundle groups together related financial operations (transactions, memberships, donations) that are part of a single payment event.\n\n**Structure:**\n- `transactions`: Array of payment transactions (required - at least one)\n- `memberships`: Array of membership subscriptions (optional)\n- `donations`: Array of donation contributions (optional)\n- `summary`: Automatically calculated counts (read-only, returned in responses only)\n\n**When Creating:**\n- Omit `id` field (auto-generated)\n- Omit `summary` (auto-calculated, returned in responses)\n- Include at least one transaction with required fields\n\n**When Updating:**\n- Include `id` field matching the bundle ID in the URL\n- Include `id` for existing items you want to modify\n- Omit `id` for new items you want to add\n- **Note:** Updates are additive - items not included in the request remain in the bundle unchanged (they are not removed)\n", properties: {id: {type: integer, description: 'Transaction bundle ID (required for updates, omitted for creation)'}, transactions: {type: array, description: 'Array of transactions in the bundle. At least one transaction is typically required.', items: {$ref: '#/components/schemas/Transaction'}}, memberships: {type: array, description: 'Array of memberships in the bundle. Optional - only include if the bundle contains memberships.', items: {$ref: '#/components/schemas/Membership'}}, donations: {type: array, description: 'Array of donations in the bundle. Optional - only include if the bundle contains donations.', items: {$ref: '#/components/schemas/Donation'}}, summary: {type: object, description: 'Summary object (read-only, automatically calculated)', additionalProperties: false, properties: {transactions_count: {type: integer}, memberships_count: {type: integer}, donations_count: {type: integer}}}}}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
        get:
            tags: ['Transaction bundles']
            summary: 'List transaction_bundles'
            description: "Retrieve a paginated list of transaction bundles for the authenticated group.\n\nResults are ordered by creation date (newest first).\n\n**Limits:**\n- Maximum `limit` value is 1000. If a higher limit is requested, it will be automatically capped at 1000.\n- If no `limit` is provided, the default maximum of 1000 will be applied.\n\n**Note:** Results are ordered by bundle creation date (newest first). A bundle that is later updated via PATCH does **not** move to the top of the list — its position is fixed at its original creation date.\n\n**Iterating to find a stop point:** Because results are ordered newest-first, when fetching a specific date range you can stop paginating as soon as you encounter a bundle older than your target date.\n"
            operationId: listTransactionBundles
            parameters: [{name: limit, in: query, description: 'Number of items to query (for pagination). Maximum value is 1000.', required: false, schema: {type: integer, maximum: 1000, default: 100}}, {name: offset, in: query, description: 'Offset of items to query (for pagination)', required: false, schema: {type: integer}}]
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: array, items: {type: object, description: "A transaction bundle groups together related financial operations (transactions, memberships, donations) that are part of a single payment event.\n\n**Structure:**\n- `transactions`: Array of payment transactions (required - at least one)\n- `memberships`: Array of membership subscriptions (optional)\n- `donations`: Array of donation contributions (optional)\n- `summary`: Automatically calculated counts (read-only, returned in responses only)\n\n**When Creating:**\n- Omit `id` field (auto-generated)\n- Omit `summary` (auto-calculated, returned in responses)\n- Include at least one transaction with required fields\n\n**When Updating:**\n- Include `id` field matching the bundle ID in the URL\n- Include `id` for existing items you want to modify\n- Omit `id` for new items you want to add\n- **Note:** Updates are additive - items not included in the request remain in the bundle unchanged (they are not removed)\n", properties: {id: {type: integer, description: 'Transaction bundle ID (required for updates, omitted for creation)'}, transactions: {type: array, description: 'Array of transactions in the bundle. At least one transaction is typically required.', items: {$ref: '#/components/schemas/Transaction'}}, memberships: {type: array, description: 'Array of memberships in the bundle. Optional - only include if the bundle contains memberships.', items: {$ref: '#/components/schemas/Membership'}}, donations: {type: array, description: 'Array of donations in the bundle. Optional - only include if the bundle contains donations.', items: {$ref: '#/components/schemas/Donation'}}, summary: {type: object, description: 'Summary object (read-only, automatically calculated)', additionalProperties: false, properties: {transactions_count: {type: integer}, memberships_count: {type: integer}, donations_count: {type: integer}}}}}}, total: {type: integer}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
    '/v1/transaction_bundles/{id}':
        get:
            tags: ['Transaction bundles']
            summary: 'Find a transaction_bundle by id'
            description: "Retrieve a specific transaction bundle by its ID, including all associated transactions, memberships, and donations.\n"
            operationId: getTransactionBundlePerId
            parameters: [{name: id, in: path, description: 'ID of transaction_bundle to return', required: true, schema: {type: integer}}]
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, description: "A transaction bundle groups together related financial operations (transactions, memberships, donations) that are part of a single payment event.\n\n**Structure:**\n- `transactions`: Array of payment transactions (required - at least one)\n- `memberships`: Array of membership subscriptions (optional)\n- `donations`: Array of donation contributions (optional)\n- `summary`: Automatically calculated counts (read-only, returned in responses only)\n\n**When Creating:**\n- Omit `id` field (auto-generated)\n- Omit `summary` (auto-calculated, returned in responses)\n- Include at least one transaction with required fields\n\n**When Updating:**\n- Include `id` field matching the bundle ID in the URL\n- Include `id` for existing items you want to modify\n- Omit `id` for new items you want to add\n- **Note:** Updates are additive - items not included in the request remain in the bundle unchanged (they are not removed)\n", properties: {id: {type: integer, description: 'Transaction bundle ID (required for updates, omitted for creation)'}, transactions: {type: array, description: 'Array of transactions in the bundle. At least one transaction is typically required.', items: {$ref: '#/components/schemas/Transaction'}}, memberships: {type: array, description: 'Array of memberships in the bundle. Optional - only include if the bundle contains memberships.', items: {$ref: '#/components/schemas/Membership'}}, donations: {type: array, description: 'Array of donations in the bundle. Optional - only include if the bundle contains donations.', items: {$ref: '#/components/schemas/Donation'}}, summary: {type: object, description: 'Summary object (read-only, automatically calculated)', additionalProperties: false, properties: {transactions_count: {type: integer}, memberships_count: {type: integer}, donations_count: {type: integer}}}}}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
        patch:
            tags: ['Transaction bundles']
            summary: 'Update an existing transaction_bundle by id'
            description: "Update an existing transaction bundle. Updates are additive for arrays — items not included in the request remain in the bundle unchanged.\n\n**Rules:**\n- To **update** an existing transaction/membership/donation: include its `id` plus ALL its required fields (not just the changed ones — omitted required fields may be cleared)\n- To **add** a new item: omit `id` — a new item will be created and added to the bundle\n- Items not included in the request arrays are left unchanged\n- The bundle `id` in the path must match the `id` in the request body\n\n> **Warning:** Always send all required fields when updating an item, not just the fields you want to change. Missing required fields on an updated item may be reset to empty/zero values.\n\n**Refund workflow**\nFor a **partial refund** (e.g. 10€ back on a 20€ transaction):\n1. Get the transaction `id` and membership/donation `id` from `GET /v1/transaction_bundles/{id}`\n2. PATCH with the reimbursed status, `reimbursed_amount`, and the updated membership/donation amount:\n```json\n{\n  \"id\": \"<bundle_id>\",\n  \"data\": {\n    \"transactions\": [\n      {\n        \"id\": \"<transaction_id>\",\n        \"contact_id\": 123456,\n        \"amount\": 2000,\n        \"currency\": \"eur\",\n        \"payment_method_kind\": \"CB\",\n        \"date\": \"2026-03-29T12:59:52+02:00\",\n        \"external_transaction_id\": 1001,\n        \"status_id\": 2,\n        \"reimbursed_amount\": 1000\n      }\n    ],\n    \"memberships\": [\n      {\n        \"id\": \"<membership_id>\",\n        \"contact_id\": 123456,\n        \"membership_price_id\": 10,\n        \"start_date\": \"2026-01-01T00:00:00Z\",\n        \"end_date\": \"2026-12-31T00:00:00Z\",\n        \"amount\": 1000,\n        \"amount_initial\": 2000,\n        \"currency\": \"eur\"\n      }\n    ]\n  }\n}\n```\nUse `GET /v1/transaction_statuses` to find the `id` of the \"Reimbursed\" and \"Cancelled\" statuses for your group.\n\nFor a **full refund or cancellation**, the cleanest approach is:\n1. `DELETE /v1/transaction_bundles/{id}` to remove the bundle\n2. `POST /v1/transaction_bundles` to recreate it with the corrected data\n\n**Note:** A PATCH does NOT change the bundle's position in `GET /v1/transaction_bundles` list — it stays at its original creation date position.\n"
            operationId: updateTransactionBundlePerId
            parameters: [{name: id, in: path, description: 'ID of transaction_bundle to update', required: true, schema: {type: integer}}]
            requestBody: {required: true, content: {application/json: {schema: {type: object, required: [data], properties: {data: {type: object, description: "A transaction bundle groups together related financial operations (transactions, memberships, donations) that are part of a single payment event.\n\n**Structure:**\n- `transactions`: Array of payment transactions (required - at least one)\n- `memberships`: Array of membership subscriptions (optional)\n- `donations`: Array of donation contributions (optional)\n- `summary`: Automatically calculated counts (read-only, returned in responses only)\n\n**When Creating:**\n- Omit `id` field (auto-generated)\n- Omit `summary` (auto-calculated, returned in responses)\n- Include at least one transaction with required fields\n\n**When Updating:**\n- Include `id` field matching the bundle ID in the URL\n- Include `id` for existing items you want to modify\n- Omit `id` for new items you want to add\n- **Note:** Updates are additive - items not included in the request remain in the bundle unchanged (they are not removed)\n", properties: {id: {type: integer, description: 'Transaction bundle ID (required for updates, omitted for creation)'}, transactions: {type: array, description: 'Array of transactions in the bundle. At least one transaction is typically required.', items: {$ref: '#/components/schemas/Transaction'}}, memberships: {type: array, description: 'Array of memberships in the bundle. Optional - only include if the bundle contains memberships.', items: {$ref: '#/components/schemas/Membership'}}, donations: {type: array, description: 'Array of donations in the bundle. Optional - only include if the bundle contains donations.', items: {$ref: '#/components/schemas/Donation'}}, summary: {type: object, description: 'Summary object (read-only, automatically calculated)', additionalProperties: false, properties: {transactions_count: {type: integer}, memberships_count: {type: integer}, donations_count: {type: integer}}}}}}}}}}
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, description: "A transaction bundle groups together related financial operations (transactions, memberships, donations) that are part of a single payment event.\n\n**Structure:**\n- `transactions`: Array of payment transactions (required - at least one)\n- `memberships`: Array of membership subscriptions (optional)\n- `donations`: Array of donation contributions (optional)\n- `summary`: Automatically calculated counts (read-only, returned in responses only)\n\n**When Creating:**\n- Omit `id` field (auto-generated)\n- Omit `summary` (auto-calculated, returned in responses)\n- Include at least one transaction with required fields\n\n**When Updating:**\n- Include `id` field matching the bundle ID in the URL\n- Include `id` for existing items you want to modify\n- Omit `id` for new items you want to add\n- **Note:** Updates are additive - items not included in the request remain in the bundle unchanged (they are not removed)\n", properties: {id: {type: integer, description: 'Transaction bundle ID (required for updates, omitted for creation)'}, transactions: {type: array, description: 'Array of transactions in the bundle. At least one transaction is typically required.', items: {$ref: '#/components/schemas/Transaction'}}, memberships: {type: array, description: 'Array of memberships in the bundle. Optional - only include if the bundle contains memberships.', items: {$ref: '#/components/schemas/Membership'}}, donations: {type: array, description: 'Array of donations in the bundle. Optional - only include if the bundle contains donations.', items: {$ref: '#/components/schemas/Donation'}}, summary: {type: object, description: 'Summary object (read-only, automatically calculated)', additionalProperties: false, properties: {transactions_count: {type: integer}, memberships_count: {type: integer}, donations_count: {type: integer}}}}}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
        delete:
            tags: ['Transaction bundles']
            summary: 'Delete a transaction bundle by id'
            operationId: deleteTransactionBundlePerId
            parameters: [{name: id, in: path, description: 'ID of transaction_bundle to delete', required: true, schema: {type: integer}}]
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
    '/v1/transaction_bundles/{id}/history':
        get:
            tags: ['Transaction bundles']
            summary: 'Returns the history of the transaction bundle'
            description: "Retrieve the complete change history for a transaction bundle. The history tracks:\n- Creation and deletion of transactions, memberships, and donations\n- Status changes for transactions\n- All modifications made to the bundle and its components\n\nEach history entry includes:\n- `kind`: Type of change (`transaction`, `transaction_status`, `donation`, `membership`)\n- `old`: Previous state (null for creations)\n- `new`: New state (null for deletions)\n- `target_id`: ID of the affected item\n"
            operationId: getHistoryOfTransactionBundle
            parameters: [{name: id, in: path, description: 'ID of transaction bundle for which the history should be fetched', required: true, schema: {type: integer}}]
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {id: {type: integer}, CreatedAt: {type: string, format: date-time}, transaction_bundle_id: {type: integer}, kind: {type: string, description: 'Describe what is stored in the entry. Can be `transaction`, `transaction_status`, `donation`, `membership` as of today'}, target_id: {type: integer}, old: {type: object, description: 'If `old` is null it describes a newly created object'}, new: {type: object, description: 'If `new` is null it describes a destroyed object'}}}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
    /v1/transaction_settings:
        get:
            tags: ['Settings and configuration']
            summary: 'Returns transaction settings of the group'
            operationId: getTransactionSettings
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {payment_method_kinds: {type: array, items: {type: string}}, max_batch_size: {type: integer}, default_status_id: {type: integer}, default_membership_price_id: {type: integer}, default_donation_price_id: {type: integer}, batch_prefix: {type: string}, batch_serial_updated_at: {type: string, format: date-time}, batch_serial: {type: integer}, membership_serial: {type: integer}, membership_prefix: {type: string}, additional_settings: {type: object}, currency: {type: string}, open_periods: {type: object}}}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
    /v1/transaction_statuses:
        get:
            tags: ['Settings and configuration']
            summary: 'List transaction statuses'
            operationId: getTransactionStatuses
            parameters: [{name: limit, in: query, description: 'Number of items to query (for pagination)', required: false, schema: {type: integer}}, {name: offset, in: query, description: 'Offset of items to query (for pagination)', required: false, schema: {type: integer}}]
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: array, items: {type: object, properties: {id: {type: integer}, CreatedAt: {type: string, format: date-time}, UpdatedAt: {type: string, format: date-time}, name: {type: string}, color: {type: string}, kind: {type: string, description: 'valid values are valid, unpaid, reimbursed, bank_error, other'}, archived: {type: boolean}}}}, total: {type: integer}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
    /v1/code_campaigns:
        get:
            tags: ['Settings and configuration']
            summary: 'List code_campaigns'
            operationId: listCodeCampaigns
            parameters: [{name: limit, in: query, description: 'Number of items to query (for pagination)', required: false, schema: {type: integer}}, {name: offset, in: query, description: 'Offset of items to query (for pagination)', required: false, schema: {type: integer}}]
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: array, items: {type: object, description: 'Given the group, the code should be unique', properties: {code: {type: string}, CreatedAt: {type: string, format: date-time}, UpdatedAt: {type: string, format: date-time}}}}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
    /v1/membership_prices:
        get:
            tags: ['Settings and configuration']
            summary: 'List membership_prices'
            operationId: listMembershipPrices
            parameters: [{name: limit, in: query, description: 'Number of items to query (for pagination)', required: false, schema: {type: integer}}, {name: offset, in: query, description: 'Offset of items to query (for pagination)', required: false, schema: {type: integer}}]
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: array, items: {type: object, properties: {id: {type: integer}, CreatedAt: {type: string, format: date-time}, UpdatedAt: {type: string, format: date-time}, amount: {type: integer, description: 'in cents'}, currency: {type: string}, allow_custom_amount: {type: boolean}, name: {type: string}, hidden: {type: boolean}, membership_price_group_id: {type: integer}, kind: {type: string, description: 'valid values are "", multi (for couples)'}, rolling_period: {type: string, description: 'rolling period, e.g. "Y" for yearly'}, duration: {type: string, description: 'duration, e.g. "Y" for yearly'}}}}, total: {type: integer}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
    /v1/membership_price_groups:
        get:
            tags: ['Settings and configuration']
            summary: 'List membership_price_groupes'
            operationId: listMembershipPriceGroups
            parameters: [{name: limit, in: query, description: 'Number of items to query (for pagination)', required: false, schema: {type: integer}}, {name: offset, in: query, description: 'Offset of items to query (for pagination)', required: false, schema: {type: integer}}]
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: array, items: {type: object, properties: {id: {type: integer}, CreatedAt: {type: string, format: date-time}, UpdatedAt: {type: string, format: date-time}, name: {type: string}, hidden: {type: boolean}, membership_prices: {type: array, items: {$ref: '#/components/schemas/MembershipPrice'}}}}}, total: {type: integer}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
    /v1/donation_prices:
        get:
            tags: ['Settings and configuration']
            summary: 'List donation_prices'
            operationId: listDonationPrices
            parameters: [{name: limit, in: query, description: 'Number of items to query (for pagination)', required: false, schema: {type: integer}}, {name: offset, in: query, description: 'Offset of items to query (for pagination)', required: false, schema: {type: integer}}]
            responses: {'200': {description: 'Successful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: array, items: {type: object, properties: {id: {type: integer}, CreatedAt: {type: string, format: date-time}, UpdatedAt: {type: string, format: date-time}, amount: {type: integer, description: 'in cents'}, currency: {type: string}, allow_custom_amount: {type: boolean}, name: {type: string}, hidden: {type: boolean}, kind: {type: string, description: 'valid values are federation, elected, donation'}}}}, total: {type: integer}}}}}}, default: {description: 'Unsuccessful operation', content: {application/json: {schema: {type: object, properties: {status: {type: string, example: fail}}}}}}}
components:
    securitySchemes:
        apiKey:
            type: http
            scheme: bearer
    schemas:
        TransactionBundle:
            type: object
            description: "A transaction bundle groups together related financial operations (transactions, memberships, donations) that are part of a single payment event.\n\n**Structure:**\n- `transactions`: Array of payment transactions (required - at least one)\n- `memberships`: Array of membership subscriptions (optional)\n- `donations`: Array of donation contributions (optional)\n- `summary`: Automatically calculated counts (read-only, returned in responses only)\n\n**When Creating:**\n- Omit `id` field (auto-generated)\n- Omit `summary` (auto-calculated, returned in responses)\n- Include at least one transaction with required fields\n\n**When Updating:**\n- Include `id` field matching the bundle ID in the URL\n- Include `id` for existing items you want to modify\n- Omit `id` for new items you want to add\n- **Note:** Updates are additive - items not included in the request remain in the bundle unchanged (they are not removed)\n"
            properties: {id: {type: integer, description: 'Transaction bundle ID (required for updates, omitted for creation)'}, transactions: {type: array, description: 'Array of transactions in the bundle. At least one transaction is typically required.', items: {$ref: '#/components/schemas/Transaction'}}, memberships: {type: array, description: 'Array of memberships in the bundle. Optional - only include if the bundle contains memberships.', items: {$ref: '#/components/schemas/Membership'}}, donations: {type: array, description: 'Array of donations in the bundle. Optional - only include if the bundle contains donations.', items: {$ref: '#/components/schemas/Donation'}}, summary: {type: object, description: 'Summary object (read-only, automatically calculated)', additionalProperties: false, properties: {transactions_count: {type: integer}, memberships_count: {type: integer}, donations_count: {type: integer}}}}
        Transaction:
            type: object
            description: "A transaction represents a single payment within a bundle.\n\n**Required fields for creation:**\n- `amount`: Payment amount in cents (e.g., 1000 = 10.00 EUR)\n- `currency`: Currency code (e.g., 'eur', 'usd')\n- `payment_method_kind`: Payment method type (e.g., 'card', 'check', 'cash', 'transfer'). Must match allowed payment methods for the group.\n- `contact_id`: Contact ID - required for transactions\n- `date`: Transaction date (ISO 8601 format)\n\n**Optional fields:**\n- `id`: Transaction ID (for updates, omit for new transactions)\n- `payment_method`: Additional payment method details (JSON object)\n- `code_campaign`: Optional campaign code (must exist in group's campaigns)\n- `delivered_at`: Optional delivery date\n- `delivery_token`: Optional delivery token\n- `comment`: Optional comment\n- `comment_date`: Optional comment date\n- `reimbursed_amount`: Reimbursed amount in cents\n- `unpaid_amount`: Unpaid amount in cents\n- `external_transaction_id`: Optional external transaction ID\n- `status_id`: Transaction status ID (must exist in the group's transaction statuses)\n"
            properties: {id: {type: integer, description: 'Transaction ID (for updates, omit for new transactions)'}, amount: {type: integer, description: 'Amount in cents (e.g., 1000 = 10.00 EUR)'}, currency: {type: string, description: 'Currency code (e.g., ''eur'', ''usd'')'}, payment_method_kind: {type: string, description: "Payment method type (e.g., 'card', 'check', 'cash', 'transfer'). Must match allowed payment methods for the group. Allowed values are **group-specific**. Retrieve your group's allowed values from `GET /v1/transaction_settings` (`payment_method_kinds` field) or `GET /v1/transaction_statuses`. A POST with an invalid value will return an error listing the valid values. Common French values: `CB`, `CBTPE`, `CHE` (chèque), `PRE` (prélèvement), `VIR` (virement), `ESP` (espèces). US/international setups may use `card`, `transfer`, `check`, `cash`.\n"}, payment_method: {type: object, description: 'Additional payment method details (JSON object)'}, contact_id: {type: integer, description: 'Contact ID - required for transactions. This is the **payer** — the contact who made the payment. Can differ from the beneficiary in `memberships` or `donations`.'}, date: {type: string, format: date-time, description: 'Transaction date (ISO 8601 format)'}, code_campaign: {type: string, description: 'Optional campaign code (must exist in group''s campaigns)'}, delivered_at: {type: string, format: date-time, description: 'Optional delivery date'}, delivery_token: {type: string, description: 'Optional delivery token'}, comment: {type: string, description: 'Optional comment'}, comment_date: {type: string, format: date-time, description: 'Optional comment date'}, reimbursed_amount: {type: integer, description: 'Reimbursed amount in cents. Used when the transaction status is ''Reimbursed''. Set to the refunded amount in cents. For a partial refund, also update `amount` on the associated `memberships` or `donations` items to keep them consistent.'}, unpaid_amount: {type: integer, description: 'Unpaid amount in cents'}, external_transaction_id: {type: integer, description: 'Optional external transaction ID. Must be a plain integer — string suffixes are not supported. Used for deduplication and external reference tracking.'}, status_id: {type: integer, description: "Transaction status ID (must exist in the group's transaction statuses). Retrieve available statuses from `GET /v1/transaction_statuses`. Each status has a `kind` (`valid`, `unpaid`, `reimbursed`, `bank_error`, `other`). When creating a transaction, omit this field or use your group's default valid status — see `GET /v1/transaction_settings` for `default_status_id`. For refunds use the status with `kind: reimbursed` paired with `reimbursed_amount`. For cancellations use the status with `kind: other` or the specific cancelled status. A special status **Tiers Payant** exists when the payer is a different person than the beneficiary.\n"}}
        Membership:
            type: object
            description: "A membership subscription within a bundle.\n\n**Required fields for creation:**\n- `contact_id`: Contact ID - required for memberships\n- `start_date`: Membership start date (ISO 8601 format)\n- `end_date`: Membership end date (ISO 8601 format)\n- `membership_price_id`: Membership price ID (must exist in group's membership prices)\n- `amount`: Amount in cents\n- `amount_initial`: Initial amount in cents\n- `currency`: Currency code\n\n**Optional fields:**\n- `id`: Membership ID (for updates, omit for new memberships)\n- `comment`: Optional comment\n- `rolling_year`: Optional rolling year\n"
            properties: {id: {type: integer, description: 'Membership ID (for updates, omit for new memberships)'}, contact_id: {type: integer, description: 'Contact ID - required for memberships. This is the **beneficiary** — the contact who receives the membership. Can differ from the payer in the parent `transaction`.'}, start_date: {type: string, format: date-time, description: 'Membership start date (ISO 8601 format)'}, end_date: {type: string, format: date-time, description: 'Membership end date (ISO 8601 format)'}, membership_price_id: {type: integer, description: 'Membership price ID (must exist in group''s membership prices)'}, amount: {type: integer, description: 'Amount in cents'}, amount_initial: {type: integer, description: 'Initial amount in cents'}, currency: {type: string, description: 'Currency code'}, comment: {type: string, description: 'Optional comment'}, rolling_year: {type: boolean, description: 'Optional rolling year'}}
        Donation:
            type: object
            description: "A donation contribution within a bundle.\n\n**Required fields for creation:**\n- `contact_id`: Contact ID - required for donations\n- `date`: Donation date (ISO 8601 format)\n- `amount`: Amount in cents\n- `initial_amount`: Initial amount in cents\n- `currency`: Currency code\n\n**Optional fields:**\n- `id`: Donation ID (for updates, omit for new donations)\n- `donation_price_id`: Donation price ID (must exist in group's donation prices)\n- `affectation`: Optional affectation code\n- `comment`: Optional comment\n\n**Note on `date`:** The `date` field on donations is optional. If omitted, the parent transaction's `date` is used.\n"
            properties: {id: {type: integer, description: 'Donation ID (for updates, omit for new donations)'}, contact_id: {type: integer, description: 'Contact ID - required for donations. This is the **beneficiary** — the contact who receives the donation benefit. Can differ from the payer in the parent `transaction`.'}, date: {type: string, format: date-time, description: 'Donation date (ISO 8601 format)'}, amount: {type: integer, description: 'Amount in cents'}, initial_amount: {type: integer, description: 'Initial amount in cents'}, currency: {type: string, description: 'Currency code'}, donation_price_id: {type: integer, description: 'Donation price ID (must exist in group''s donation prices)'}, affectation: {type: string, description: 'Optional affectation code'}, comment: {type: string, description: 'Optional comment'}}
        ContactForTransaction:
            type: object
            properties: {id: {type: integer}, firstname: {type: string}, surname: {type: string}, membership_number: {type: integer}}
        UserForTransaction:
            type: object
            properties: {id: {type: integer}, firstname: {type: string}, surname: {type: string}, avatar: {type: string}}
        TransactionBundleHistoryEntry:
            type: object
            properties: {id: {type: integer}, CreatedAt: {type: string, format: date-time}, transaction_bundle_id: {type: integer}, kind: {type: string, description: 'Describe what is stored in the entry. Can be `transaction`, `transaction_status`, `donation`, `membership` as of today'}, target_id: {type: integer}, old: {type: object, description: 'If `old` is null it describes a newly created object'}, new: {type: object, description: 'If `new` is null it describes a destroyed object'}}
        TransactionSettings:
            type: object
            properties: {payment_method_kinds: {type: array, items: {type: string}}, max_batch_size: {type: integer}, default_status_id: {type: integer}, default_membership_price_id: {type: integer}, default_donation_price_id: {type: integer}, batch_prefix: {type: string}, batch_serial_updated_at: {type: string, format: date-time}, batch_serial: {type: integer}, membership_serial: {type: integer}, membership_prefix: {type: string}, additional_settings: {type: object}, currency: {type: string}, open_periods: {type: object}}
        TransactionStatus:
            type: object
            properties: {id: {type: integer}, CreatedAt: {type: string, format: date-time}, UpdatedAt: {type: string, format: date-time}, name: {type: string}, color: {type: string}, kind: {type: string, description: 'valid values are valid, unpaid, reimbursed, bank_error, other'}, archived: {type: boolean}}
        DonationPrice:
            type: object
            properties: {id: {type: integer}, CreatedAt: {type: string, format: date-time}, UpdatedAt: {type: string, format: date-time}, amount: {type: integer, description: 'in cents'}, currency: {type: string}, allow_custom_amount: {type: boolean}, name: {type: string}, hidden: {type: boolean}, kind: {type: string, description: 'valid values are federation, elected, donation'}}
        MembershipPrice:
            type: object
            properties: {id: {type: integer}, CreatedAt: {type: string, format: date-time}, UpdatedAt: {type: string, format: date-time}, amount: {type: integer, description: 'in cents'}, currency: {type: string}, allow_custom_amount: {type: boolean}, name: {type: string}, hidden: {type: boolean}, membership_price_group_id: {type: integer}, kind: {type: string, description: 'valid values are "", multi (for couples)'}, rolling_period: {type: string, description: 'rolling period, e.g. "Y" for yearly'}, duration: {type: string, description: 'duration, e.g. "Y" for yearly'}}
        MembershipPriceGroup:
            type: object
            properties: {id: {type: integer}, CreatedAt: {type: string, format: date-time}, UpdatedAt: {type: string, format: date-time}, name: {type: string}, hidden: {type: boolean}, membership_prices: {type: array, items: {$ref: '#/components/schemas/MembershipPrice'}}}
        CodeCampaign:
            type: object
            description: 'Given the group, the code should be unique'
            properties: {code: {type: string}, CreatedAt: {type: string, format: date-time}, UpdatedAt: {type: string, format: date-time}}
