# Authentication Source: https://docs.gettrxn.com/api-reference/authentication Learn how to authenticate with the TRXN API using bearer tokens, handle two-factor authentication, and manage sandbox environments. TRXN uses token-based authentication for all API requests. Every API call (except authentication itself) requires a valid API token. ## Overview | Aspect | Details | | ------------------------- | ------------------------------------------ | | **Authentication method** | Bearer token | | **Header** | `Authorization: Bearer YOUR_TOKEN` | | **Token format** | 32-character hex string | | **Account scoping** | Each token is tied to one account | | **Sandbox support** | Tokens can be scoped to a specific sandbox | ## Obtaining an API token ### Option 1: Via the dashboard (recommended) 1. Navigate to **API Tokens** in the account settings. 2. Click **New API Token**. 3. Enter a name for the token. 4. The token value is displayed once after creation -- copy it immediately. The token value is only displayed once at creation time. If you lose it, you will need to create a new token. ### Option 2: Via the auth endpoint Exchange email and password credentials for an API token programmatically. ``` POST /v1/auth ``` The user's email address. The user's password. The six-digit one-time password from the user's authenticator app. Required when the user has two-factor authentication enabled. #### Request ```bash cURL theme={null} curl -X POST https://api.gettrxn.com/v1/auth \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "your_password" }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.gettrxn.com/v1/auth", json={ "email": "user@example.com", "password": "your_password" } ) data = response.json() ``` ```javascript Node.js theme={null} const response = await fetch("https://api.gettrxn.com/v1/auth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: "user@example.com", password: "your_password" }) }); const data = await response.json(); ``` #### Success response (200 OK) ```json theme={null} { "token": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" } ``` #### Error responses **Invalid credentials (401 Unauthorized):** ```json theme={null} { "error": "Invalid Email or password." } ``` **Two-factor authentication required (422 Unprocessable Entity):** ```json theme={null} { "error": "otp_attempt_required" } ``` When the user has two-factor authentication enabled, include the `otp_attempt` parameter: ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/auth \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "your_password", "otp_attempt": "123456" }' ``` **Invalid OTP code (401 Unauthorized):** ```json theme={null} { "error": "Incorrect verification code" } ``` ## Authenticating API requests Include the token in the `Authorization` header of every API request: ```bash theme={null} curl https://api.gettrxn.com/v1/customers \ -H "Authorization: Bearer YOUR_TOKEN" ``` ### Authentication header format ``` Authorization: Bearer YOUR_TOKEN ``` The API also accepts the `token` keyword: ``` Authorization: token YOUR_TOKEN ``` ### Missing or invalid token (401 Unauthorized) Requests without a valid token receive an empty `401 Unauthorized` response: ``` HTTP/1.1 401 Unauthorized ``` No JSON body is returned for missing or invalid tokens. ## Account scoping Each API token is tied to a specific account. All API requests are automatically scoped to that account's data: * **Customers** returned are only those belonging to the token's account. * **Invoices**, **products**, **prices**, and other resources are similarly scoped. * Tokens cannot access data from other accounts. ### Token usage tracking Each time a token is used for authentication, its `last_used_at` timestamp is updated. This is visible in the dashboard for auditing purposes. ## Sandbox mode API tokens can be created within a sandbox environment for testing purposes. Production tokens access only production data. Sandbox tokens access only sandbox data. The sandbox association is set when the token is created. ### How it works * **Production tokens** (no sandbox) access only production data. * **Sandbox tokens** access only sandbox data. * The sandbox association is set when the token is created. ### Creating a sandbox token Create an API token while in sandbox mode through the dashboard. The token will automatically be scoped to that sandbox and will only return sandbox data. ## Token management ### Listing tokens View all API tokens for an account in the dashboard at **API Tokens**. ### Revoking a token Delete an API token from the dashboard to immediately revoke access. Any requests using that token will receive a `401 Unauthorized` response. ## Code examples ```ruby Ruby theme={null} require "net/http" require "json" uri = URI("https://api.gettrxn.com/v1/customers") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Get.new(uri) request["Authorization"] = "Bearer YOUR_TOKEN" response = http.request(request) data = JSON.parse(response.body) ``` ```python Python theme={null} import requests headers = { "Authorization": "Bearer YOUR_TOKEN" } response = requests.get( "https://api.gettrxn.com/v1/customers", headers=headers ) data = response.json() ``` ```javascript Node.js theme={null} const response = await fetch("https://api.gettrxn.com/v1/customers", { headers: { "Authorization": "Bearer YOUR_TOKEN" } }); const data = await response.json(); ``` ```bash cURL theme={null} # List customers curl https://api.gettrxn.com/v1/customers \ -H "Authorization: Bearer YOUR_TOKEN" # Create a customer curl -X POST https://api.gettrxn.com/v1/customers \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"email": "customer@example.com", "first_name": "Jane", "last_name": "Doe"}' ``` ## Available API endpoints All endpoints require authentication unless noted otherwise. | Endpoint | Methods | Description | | ----------------------------------------- | ----------------- | ----------------------------------- | | `/v1/auth` | POST | Obtain API token (no auth required) | | `/v1/me` | GET | Current user info | | `/v1/accounts` | CRUD | Account management | | `/v1/customers` | CRUD | Customer management | | `/v1/products` | CRUD | Product management | | `/v1/prices` | CRUD | Price management | | `/v1/invoices` | CRUD | Invoice management | | `/v1/subscriptions` | CRUD | Subscription management | | `/v1/webhook_endpoints` | CRUD | Webhook endpoint management | | `/v1/crypto_transactions` | GET, POST | Crypto transaction records | | `/v1/crypto_addresses` | GET, POST, DELETE | Crypto address management | | `/v1/crypto_payment_claims` | GET | Payment claim records | | `/v1/crypto_payment_claims/:id/approval` | POST | Approve a claim | | `/v1/crypto_payment_claims/:id/rejection` | POST | Reject a claim | | `/v1/wallets` | GET | Wallet information | | `/v1/payment_claim_links` | GET, POST, DELETE | Payment claim links | ## Best practices Follow these guidelines to keep your API tokens secure and your integration reliable. 1. **Store tokens securely** -- never commit tokens to source control or expose them in client-side code. 2. **Use environment variables** to store tokens in your application. 3. **Create separate tokens** for different integrations or environments. 4. **Revoke unused tokens** promptly when they are no longer needed. 5. **Use sandbox tokens** for development and testing to avoid affecting production data. 6. **Monitor usage** by checking `last_used_at` in the dashboard for unusual activity. # Crypto addresses Source: https://docs.gettrxn.com/api-reference/crypto-addresses Create and manage cryptocurrency addresses for receiving payments. Addresses are validated by currency and belong to wallets. This API allows you to create and manage cryptocurrency addresses for receiving payments. ## The crypto address object Unique identifier with `addr_` prefix (e.g., `addr_abc123def456`). Always `"crypto_address"`. The cryptocurrency address. Currency code (e.g., `BTC`, `ETH`). The wallet ID this address belongs to. Blockchain explorer information. Name of the blockchain explorer (e.g., `Blockchain.com`, `Etherscan`). URL to view the address on the explorer. When the address was created (ISO 8601). When the address was last updated (ISO 8601). ## List crypto addresses ``` GET /v1/crypto_addresses ``` Retrieves a paginated list of all crypto addresses in your account. Addresses are ordered by creation date (newest first). Page number for pagination. Results are returned 25 items per page. ```bash theme={null} curl https://api.gettrxn.com/v1/crypto_addresses \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "crypto_addresses": [ { "id": "addr_abc123def456", "object": "crypto_address", "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "currency": "BTC", "wallet_id": "wall_xyz789", "explorer": { "name": "Blockchain.com", "url": "https://www.blockchain.com/explorer/addresses/btc/bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" }, "created_at": "2025-01-25T12:00:00Z", "updated_at": "2025-01-25T12:00:00Z" } ], "pagination": { "page": 1, "pages": 3, "count": 67 } } ``` ## Get crypto address ``` GET /v1/crypto_addresses/:id ``` Retrieves a specific crypto address by ID. The address's ID (e.g., `addr_abc123def456`). ```bash theme={null} curl https://api.gettrxn.com/v1/crypto_addresses/addr_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "addr_abc123def456", "object": "crypto_address", "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "currency": "BTC", "wallet_id": "wall_xyz789", "explorer": { "name": "Blockchain.com", "url": "https://www.blockchain.com/explorer/addresses/btc/bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" }, "created_at": "2025-01-25T12:00:00Z", "updated_at": "2025-01-25T12:00:00Z" } ``` ## Create crypto address ``` POST /v1/crypto_addresses ``` Creates a new crypto address in a wallet. The wallet can be either an account wallet or a customer wallet. The wallet's ID (e.g., `wall_xyz789`). Must belong to your account. The currency code (e.g., `BTC`, `ETH`). Must be a supported currency. The cryptocurrency address. Validated based on the currency format. ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/crypto_addresses \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "wallet_id": "wall_xyz789", "currency_code": "BTC", "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" }' ``` ```json Response theme={null} { "id": "addr_new123abc456", "object": "crypto_address", "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "currency": "BTC", "wallet_id": "wall_xyz789", "explorer": { "name": "Blockchain.com", "url": "https://www.blockchain.com/explorer/addresses/btc/bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" }, "created_at": "2025-01-25T14:30:00Z", "updated_at": "2025-01-25T14:30:00Z" } ``` ### Error responses ```json Wallet not found theme={null} { "error": { "type": "invalid_request_error", "code": "resource_not_found", "message": "Wallet not found", "param": "wallet_id" } } ``` ```json Unsupported currency theme={null} { "error": { "type": "invalid_request_error", "code": "resource_not_found", "message": "Currency not found: DOGE", "param": "currency_code" } } ``` ```json Duplicate address theme={null} { "error": { "type": "invalid_request_error", "code": "parameter_invalid", "message": "Address is already in use for this currency in your account", "param": "address" } } ``` ## Delete crypto address ``` DELETE /v1/crypto_addresses/:id ``` Deletes a crypto address from your account. Deleting an address will also delete any associated payment claims and transaction records. The address's ID (e.g., `addr_abc123def456`). ```bash theme={null} curl -X DELETE https://api.gettrxn.com/v1/crypto_addresses/addr_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` Returns `204 No Content` on successful deletion. ## Address validation Addresses are validated based on their currency: * **BTC**: Validates Bitcoin address formats (legacy, SegWit, native SegWit). * **ETH**: Validates Ethereum address format (`0x` followed by 40 hex characters). ## Supported currencies | Currency | Explorer | | -------- | -------------- | | `BTC` | Blockchain.com | | `ETH` | Etherscan | ## Sandbox support The API respects sandbox scoping. If your API token is associated with a sandbox, you can only access addresses created in that sandbox. Address uniqueness is enforced separately within each sandbox. # Crypto transactions Source: https://docs.gettrxn.com/api-reference/crypto-transactions View and manually create cryptocurrency transactions for your account. Transactions include allocation information showing how payments are applied to invoices. This API allows you to view and manually create cryptocurrency transactions for your account. ## The crypto transaction object Unique identifier with `trxn_` prefix (e.g., `trxn_abc123def456`). Always `"crypto_transaction"`. Transaction amount (string to preserve precision). Blockchain transaction hash. Source of the transaction: `manual_entry` or `blockchain`. When the transaction occurred (ISO 8601). Currency code (e.g., `BTC`, `ETH`). Sending address ID. May be `null`. Sending address. May be `null`. Receiving address ID. Receiving address. Transaction allocations to invoices. Unique identifier for the allocation. Invoice ID this allocation applies to. Amount applied to the invoice. When the allocation was created (ISO 8601). When the record was created (ISO 8601). When the record was last updated (ISO 8601). ## List crypto transactions ``` GET /v1/crypto_transactions ``` Retrieves a paginated list of all crypto transactions in your account. Transactions are ordered by timestamp (newest first). Page number for pagination. Results are returned 25 items per page. ```bash theme={null} curl https://api.gettrxn.com/v1/crypto_transactions \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "crypto_transactions": [ { "id": "trxn_abc123def456", "object": "crypto_transaction", "amount": "0.5", "transaction_id": "0x1234567890abcdef...", "transaction_source": "manual_entry", "timestamp": "2025-01-25T12:00:00Z", "currency": "BTC", "from_address_id": "addr_xyz789", "from_address": "bc1q...", "to_address_id": "addr_abc123", "to_address": "bc1q...", "allocations": [ { "id": "alloc_123", "invoice_id": "inv_456", "amount_applied": "0.25", "created_at": "2025-01-25T12:30:00Z" } ], "created_at": "2025-01-25T12:00:00Z", "updated_at": "2025-01-25T12:00:00Z" } ], "pagination": { "page": 1, "pages": 3, "count": 67 } } ``` ## Get crypto transaction ``` GET /v1/crypto_transactions/:id ``` Retrieves a specific crypto transaction by ID, including its allocations. The transaction's ID (e.g., `trxn_abc123def456`). ```bash theme={null} curl https://api.gettrxn.com/v1/crypto_transactions/trxn_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "trxn_abc123def456", "object": "crypto_transaction", "amount": "0.5", "transaction_id": "0x1234567890abcdef...", "transaction_source": "manual_entry", "timestamp": "2025-01-25T12:00:00Z", "currency": "BTC", "from_address_id": "addr_xyz789", "from_address": "bc1q...", "to_address_id": "addr_abc123", "to_address": "bc1q...", "allocations": [ { "id": "alloc_123", "invoice_id": "inv_456", "amount_applied": "0.25", "created_at": "2025-01-25T12:30:00Z" } ], "created_at": "2025-01-25T12:00:00Z", "updated_at": "2025-01-25T12:00:00Z" } ``` ## Create crypto transaction ``` POST /v1/crypto_transactions ``` Creates a new crypto transaction manually. This is useful for recording transactions that were not automatically detected by the system. The receiving address ID (e.g., `addr_abc123`). Must belong to your account. The transaction amount (as a string to preserve precision). The currency code (e.g., `BTC`, `ETH`). The blockchain transaction hash/ID. The transaction timestamp (ISO 8601 format). Defaults to current time. The sending address ID (e.g., `addr_xyz789`). ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/crypto_transactions \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "to_address_id": "addr_abc123", "amount": "0.5", "currency_code": "BTC", "transaction_id": "0x1234567890abcdef...", "timestamp": "2025-01-25T12:00:00Z" }' ``` ```json Response theme={null} { "id": "trxn_new123abc456", "object": "crypto_transaction", "amount": "0.5", "transaction_id": "0x1234567890abcdef...", "transaction_source": "manual_entry", "timestamp": "2025-01-25T12:00:00Z", "currency": "BTC", "from_address_id": null, "from_address": null, "to_address_id": "addr_abc123", "to_address": "bc1q...", "allocations": [], "created_at": "2025-01-25T14:30:00Z", "updated_at": "2025-01-25T14:30:00Z" } ``` ### Error responses ```json Address not found theme={null} { "error": { "type": "invalid_request_error", "code": "resource_not_found", "message": "Crypto address not found", "param": "to_address_id" } } ``` ```json Unsupported currency theme={null} { "error": { "type": "invalid_request_error", "code": "resource_not_found", "message": "Currency not found: DOGE", "param": "currency_code" } } ``` ## The allocation object | Field | Type | Description | | ---------------- | ------ | ------------------------------------------- | | `id` | string | Unique identifier for the allocation. | | `invoice_id` | string | Invoice ID this allocation applies to. | | `amount_applied` | string | Amount applied to the invoice. | | `created_at` | string | When the allocation was created (ISO 8601). | ## Sandbox support The API respects sandbox scoping. If your API token is associated with a sandbox, you can only access transactions created in that sandbox. Manually created transactions will be isolated from production data. # Customers Source: https://docs.gettrxn.com/api-reference/customers Create and manage customers for your account. Customers are the entities that make cryptocurrency payments for your products and services. This API allows you to create and manage customers for your account. Customers are the entities that will make cryptocurrency payments for your products and services. ## The customer object Unique identifier with `cust_` prefix (e.g., `cust_abc123def456`). The customer's email address. Automatically converted to lowercase. The customer's first name. The customer's last name. When the customer was created (ISO 8601). When the customer was last updated (ISO 8601). ## List customers ``` GET /v1/customers ``` Retrieves a paginated list of all customers in your account. Page number for pagination. Results are returned 25 items per page. ```bash theme={null} curl https://api.gettrxn.com/v1/customers \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "customers": [ { "id": "cust_abc123def456", "email": "john.doe@example.com", "first_name": "John", "last_name": "Doe", "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T12:00:00Z" }, { "id": "cust_xyz789ghi012", "email": "jane.smith@example.com", "first_name": "Jane", "last_name": "Smith", "created_at": "2024-01-02T10:30:00Z", "updated_at": "2024-01-02T10:30:00Z" } ], "pagination": { "page": 1, "pages": 3, "count": 67 } } ``` ## Get customer ``` GET /v1/customers/:id ``` Retrieves a specific customer by ID. The customer's ID (e.g., `cust_abc123def456`). ```bash theme={null} curl https://api.gettrxn.com/v1/customers/cust_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "cust_abc123def456", "email": "john.doe@example.com", "first_name": "John", "last_name": "Doe", "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T12:00:00Z" } ``` ## Create customer ``` POST /v1/customers ``` Creates a new customer in your account. A cryptocurrency wallet is automatically created for the customer. The customer's email address. Must be a valid email format. Automatically converted to lowercase. Must be unique within your account. The customer's first name. The customer's last name. ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/customers \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "new.customer@example.com", "first_name": "New", "last_name": "Customer" }' ``` ```json Response theme={null} { "id": "cust_new123customer456", "email": "new.customer@example.com", "first_name": "New", "last_name": "Customer", "created_at": "2024-01-15T14:30:00Z", "updated_at": "2024-01-15T14:30:00Z" } ``` ### Validation errors ```json Email cannot be blank theme={null} { "errors": { "email": ["can't be blank"] } } ``` ```json Invalid email format theme={null} { "errors": { "email": ["is invalid"] } } ``` ```json Duplicate email theme={null} { "errors": { "email": ["has already been taken"] } } ``` ## Update customer ``` PATCH /v1/customers/:id ``` Updates an existing customer. The customer's ID (e.g., `cust_abc123def456`). The customer's email address. Must be a valid email format. The customer's first name. The customer's last name. ```bash theme={null} curl -X PATCH https://api.gettrxn.com/v1/customers/cust_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "updated.email@example.com", "first_name": "Updated" }' ``` ```json Response theme={null} { "id": "cust_abc123def456", "email": "updated.email@example.com", "first_name": "Updated", "last_name": "Doe", "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-15T16:45:00Z" } ``` ## Delete customer ``` DELETE /v1/customers/:id ``` Deletes a customer from your account. This action is irreversible and will also delete the customer's associated wallet and crypto addresses. The customer's ID (e.g., `cust_abc123def456`). ```bash theme={null} curl -X DELETE https://api.gettrxn.com/v1/customers/cust_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` Returns `204 No Content` on successful deletion. ## Customer features ### Automatic wallet creation When a customer is created, a cryptocurrency wallet is automatically created for them. This wallet can hold multiple cryptocurrency addresses for receiving payments. ### Email validation * Email addresses must be in a valid format. * Email addresses are automatically converted to lowercase. * Email addresses must be unique within your account (but can be duplicated across different accounts). ### Prefixed IDs All customer IDs are returned with the prefix `cust_` followed by a unique identifier (e.g., `cust_abc123def456`). This makes it easy to identify customer IDs in your application. ## Pagination By default, the API returns 25 customers per page. Use the `page` query parameter to navigate through pages. The `pagination` object in the response includes: Current page number. Total number of pages. Total number of items across all pages. ## Integration example ```bash theme={null} # 1. Create a customer curl -X POST https://api.gettrxn.com/v1/customers \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"email": "customer@example.com", "first_name": "John", "last_name": "Doe"}' # Response: {"id": "cust_abc123def456", ...} # 2. Create a payment claim link for the customer curl -X POST https://api.gettrxn.com/v1/payment_claim_links \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"customer_id": "cust_abc123def456"}' # 3. Send the payment link to the customer # 4. Customer submits payment information # 5. Review and approve the payment claim ``` ## Sandbox support The API respects sandbox scoping. If your API token is associated with a sandbox, you can only access customers created in that sandbox. Email uniqueness is enforced separately within each sandbox. # Errors Source: https://docs.gettrxn.com/api-reference/errors Understand the TRXN API error response format, HTTP status codes, error types, error codes, and how to handle errors in your integration. All API errors are returned in a consistent JSON format with an `error` object containing structured information about what went wrong. The error format follows Stripe's API error conventions for consistency and familiarity. ## Error response structure ### Response format ```json theme={null} { "error": { "type": "invalid_request_error", "code": "resource_not_found", "message": "Customer not found", "param": "customer_id", "timestamp": "2025-01-13T12:00:00Z", "doc_url": "https://docs.gettrxn.com/errors#resource-not-found" } } ``` ### Error attributes The category of error. See [error types](#error-types) below. A short string indicating the specific error. See [error codes](#error-codes) below. A human-readable message providing details about the error. If the error is parameter-specific, the name of the parameter related to the error. May be `null`. ISO 8601 timestamp of when the error occurred. URL to documentation about this error type. ## HTTP status codes The API uses conventional HTTP response codes to indicate success or failure: | Code | Status | Description | | ---- | --------------------- | ------------------------------------------------------------ | | 200 | OK | Request succeeded. | | 201 | Created | Resource was successfully created. | | 204 | No Content | Request succeeded with no response body (e.g., DELETE). | | 400 | Bad Request | Invalid request syntax or missing required parameters. | | 401 | Unauthorized | No valid API token provided. | | 403 | Forbidden | The API token lacks permissions for the requested operation. | | 404 | Not Found | The requested resource doesn't exist or is not accessible. | | 422 | Unprocessable Entity | Request was valid but contained invalid parameter values. | | 429 | Too Many Requests | Rate limit exceeded. | | 500 | Internal Server Error | Something went wrong on the server. | ## Error types The `type` field categorizes the error: | Type | Description | | ----------------------- | ------------------------------------------------------------------------------------------------- | | `invalid_request_error` | The request had invalid parameters or referenced a non-existent resource. Most common error type. | | `api_error` | Server-side errors or temporary issues. These are rare. | | `authentication_error` | Invalid or missing API credentials. | | `address_error` | Cryptocurrency address validation failures. | ## Error codes The `code` field provides a programmatic identifier for handling specific errors: | Code | HTTP Status | Description | | ----------------------- | ----------- | ---------------------------------------------------------------------- | | `resource_not_found` | 404 | The requested resource (customer, invoice, price, etc.) was not found. | | `parameter_missing` | 400 | A required parameter was not provided. | | `parameter_invalid` | 422 | A parameter value was invalid or failed validation. | | `invalid_credentials` | 401 | The API token is invalid or expired. | | `address_invalid` | 422 | The cryptocurrency address is invalid. | | `rate_limit_error` | 429 | Too many requests in a short period. | | `verification_failed` | 422 | Verification (e.g., Turnstile) failed. | | `external_api_error` | 502 | An external service dependency failed. | | `internal_server_error` | 500 | An unexpected server error occurred. | ## Common error examples ### Resource not found (404) When requesting a resource that doesn't exist or belongs to another account: ```bash theme={null} GET /api/v1/customers/cus_nonexistent ``` ```json Response theme={null} { "error": { "type": "invalid_request_error", "code": "resource_not_found", "message": "Customer not found", "param": "id", "timestamp": "2025-01-13T12:00:00Z", "doc_url": "https://docs.gettrxn.com/errors#resource-not-found" } } ``` ### Validation error (422) When creating or updating a resource with invalid data: ```bash theme={null} POST /api/v1/customers Content-Type: application/json {"email": "invalid-email"} ``` ```json Response theme={null} { "error": { "type": "invalid_request_error", "code": "parameter_invalid", "message": "Email is invalid", "param": "email", "timestamp": "2025-01-13T12:00:00Z", "doc_url": "https://docs.gettrxn.com/errors#parameter-invalid" } } ``` ### Nested resource not found (404) When a nested resource reference is invalid: ```bash theme={null} POST /api/v1/invoices Content-Type: application/json { "customer_id": "cus_abc123", "line_items": [ {"price_id": "pri_nonexistent", "quantity": 1} ] } ``` ```json Response theme={null} { "error": { "type": "invalid_request_error", "code": "resource_not_found", "message": "Price not found: pri_nonexistent", "param": "line_items[][price_id]", "timestamp": "2025-01-13T12:00:00Z", "doc_url": "https://docs.gettrxn.com/errors#resource-not-found" } } ``` ### Authentication error (401) When the API token is missing or invalid: ```bash theme={null} GET /api/v1/customers # No Authorization header ``` ```json Response theme={null} { "error": { "type": "authentication_error", "code": "invalid_credentials", "message": "Invalid or missing API token", "param": null, "timestamp": "2025-01-13T12:00:00Z", "doc_url": "https://docs.gettrxn.com/errors#invalid-credentials" } } ``` ## Handling errors ### Programmatic error handling Use the `type` and `code` fields to handle errors programmatically: ```ruby Ruby theme={null} response = api_client.create_invoice(params) if response.error? case response.error.code when "resource_not_found" # Handle missing resource (customer, price, etc.) log_error("Resource not found: #{response.error.param}") when "parameter_invalid" # Handle validation errors show_validation_error(response.error.param, response.error.message) when "rate_limit_error" # Implement exponential backoff retry_with_backoff else # Handle unexpected errors log_and_alert(response.error) end end ``` ```javascript Node.js theme={null} try { await createInvoice(invoiceData); } catch (error) { if (error.response?.data?.error) { const apiError = error.response.data.error; // Display the message to the user showNotification(apiError.message, 'error'); // Highlight the problematic field if param is set if (apiError.param) { highlightField(apiError.param); } } } ``` ## Best practices Use the `code` field for programmatic handling, not the `message` field. Messages may change over time while codes remain stable. 1. **Always check for the `error` object** in non-2xx responses. 2. **Use `code` for programmatic handling**, not `message` (messages may change). 3. **Display `message` to users** -- it is designed to be human-readable. 4. **Use `param` to highlight form fields** that need correction. 5. **Log `timestamp` and full error** for debugging. 6. **Implement exponential backoff** for `rate_limit_error`. 7. **Check `doc_url`** for detailed documentation about specific errors. ## Rate limiting When you exceed rate limits, you will receive: ```json theme={null} { "error": { "type": "invalid_request_error", "code": "rate_limit_error", "message": "Too many requests. Please retry after 60 seconds.", "param": null, "timestamp": "2025-01-13T12:00:00Z", "doc_url": "https://docs.gettrxn.com/errors#rate-limit-error" } } ``` Implement exponential backoff when retrying rate-limited requests. Do not retry immediately or in a tight loop. ## Support For questions about API errors or unexpected error responses, contact support with: * The full error response JSON * The request that caused the error (excluding sensitive data) * The timestamp of the error # Invoices Source: https://docs.gettrxn.com/api-reference/invoices Create and manage invoices for your account. Invoices represent billing documents sent to customers with line items for products and services. This API allows you to create and manage invoices for your account. Invoices represent billing documents sent to customers with line items for products and services. ## The invoice object Unique identifier with `inv_` prefix (e.g., `inv_abc123def456`). The customer's ID associated with this invoice. The associated subscription ID. `null` for standalone invoices. Invoice status: `pending`, `paid`, `overdue`, or `canceled`. The invoice due date (format: `YYYY-MM-DD`). Total amount for the invoice. Array of line items on the invoice. Unique identifier for the line item. The price ID referenced by this line item. The product ID for this line item. The product name. Quantity of this item. Price per unit. Total amount for this line item (unit\_amount x quantity). When the invoice was created (ISO 8601). When the invoice was last updated (ISO 8601). ## List invoices ``` GET /v1/invoices ``` Retrieves a paginated list of all invoices in your account. Page number for pagination. Results are returned 25 items per page. ```bash theme={null} curl https://api.gettrxn.com/v1/invoices \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "invoices": [ { "id": "inv_abc123def456", "customer_id": "cus_xyz789", "subscription_id": null, "status": "pending", "due_date": "2025-02-28", "total_amount": "199.98", "created_at": "2025-01-15T12:00:00Z", "updated_at": "2025-01-15T12:00:00Z", "line_items": [ { "id": "li_item123", "price_id": "pri_abc123", "product_id": "pro_xyz789", "product_name": "Premium Plan", "quantity": 2, "unit_amount": "99.99", "amount": "199.98" } ] } ], "pagination": { "page": 1, "pages": 3, "count": 67 } } ``` ## Get invoice ``` GET /v1/invoices/:id ``` Retrieves a specific invoice by ID with all line items. The invoice's ID (e.g., `inv_abc123def456`). ```bash theme={null} curl https://api.gettrxn.com/v1/invoices/inv_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "inv_abc123def456", "customer_id": "cus_xyz789", "subscription_id": "sub_def456", "status": "pending", "due_date": "2025-02-28", "total_amount": "99.99", "created_at": "2025-01-15T12:00:00Z", "updated_at": "2025-01-15T12:00:00Z", "line_items": [ { "id": "li_item123", "price_id": "pri_abc123", "product_id": "pro_xyz789", "product_name": "Premium Plan", "quantity": 1, "unit_amount": "99.99", "amount": "99.99" } ] } ``` ## Create invoice ``` POST /v1/invoices ``` Creates a new invoice with optional line items. The customer's ID (e.g., `cus_xyz789`). Must belong to your account. The invoice due date (format: `YYYY-MM-DD`). Invoice status. One of: `pending`, `paid`, `overdue`, `canceled`. Associated subscription ID. Array of line item objects. The price ID for the line item. Must belong to your account. Quantity for this line item. Override amount for the line item. ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/invoices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cus_xyz789", "due_date": "2025-02-28", "line_items": [ {"price_id": "pri_abc123", "quantity": 2} ] }' ``` ```json Response theme={null} { "id": "inv_new123invoice456", "customer_id": "cus_xyz789", "subscription_id": null, "status": "pending", "due_date": "2025-02-28", "total_amount": "199.98", "created_at": "2025-01-15T14:30:00Z", "updated_at": "2025-01-15T14:30:00Z", "line_items": [ { "id": "li_new123", "price_id": "pri_abc123", "product_id": "pro_xyz789", "product_name": "Premium Plan", "quantity": 2, "unit_amount": "99.99", "amount": "199.98" } ] } ``` ### Error responses ```json Customer not found theme={null} { "error": "Customer not found" } ``` ```json Price not found theme={null} { "error": "Price not found: pri_invalid123" } ``` ## Update invoice ``` PATCH /v1/invoices/:id ``` Updates an existing invoice's status or due date. The invoice's ID. Invoice status. One of: `pending`, `paid`, `overdue`, `canceled`. The invoice due date (format: `YYYY-MM-DD`). ```bash theme={null} curl -X PATCH https://api.gettrxn.com/v1/invoices/inv_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "status": "paid" }' ``` ```json Response theme={null} { "id": "inv_abc123def456", "customer_id": "cus_xyz789", "status": "paid", "due_date": "2025-02-28", "total_amount": "99.99", "created_at": "2025-01-15T12:00:00Z", "updated_at": "2025-01-16T10:00:00Z", "line_items": [...] } ``` ## Delete invoice ``` DELETE /v1/invoices/:id ``` Deletes an invoice from your account. The invoice's ID. ```bash theme={null} curl -X DELETE https://api.gettrxn.com/v1/invoices/inv_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` Returns `204 No Content` on successful deletion. ## Invoice status values | Status | Description | | ---------- | ------------------------------------------ | | `pending` | Invoice has been created but not yet paid. | | `paid` | Invoice has been fully paid. | | `overdue` | Invoice is past its due date. | | `canceled` | Invoice has been canceled. | ## Usage flow 1. **Create a customer** using the Customers API. 2. **Create products and prices** using the Products and Prices APIs. 3. **Create an invoice** with line items referencing your prices. 4. **Send the invoice** to your customer (via your application). 5. **Update status to paid** when payment is received. 6. **Track overdue invoices** and follow up as needed. ## Integration example ```bash theme={null} # 1. Create a customer curl -X POST https://api.gettrxn.com/v1/customers \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"email": "customer@example.com"}' # Response: {"id": "cus_abc123", ...} # 2. Create an invoice for the customer curl -X POST https://api.gettrxn.com/v1/invoices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cus_abc123", "due_date": "2025-02-28", "line_items": [ {"price_id": "pri_monthly_plan", "quantity": 1} ] }' # 3. Mark invoice as paid when payment received curl -X PATCH https://api.gettrxn.com/v1/invoices/inv_xyz789 \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"status": "paid"}' ``` ## Sandbox support The API respects sandbox scoping. If your API token is associated with a sandbox, you can only access invoices created in that sandbox. Invoices created in sandbox mode will be isolated from production data. # Pagination Source: https://docs.gettrxn.com/api-reference/pagination Navigate large collections of resources with page-based pagination. All list endpoints in the TRXN API return paginated results. The default page size is 25 items per page. ## Request parameters The page number to retrieve. Pages are 1-indexed. ## Making a paginated request ```bash theme={null} curl https://api.gettrxn.com/v1/customers?page=2 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ## Response format Every paginated response includes a `pagination` object alongside the resource array: ```json theme={null} { "customers": [ {"id": "cust_abc123", "email": "alice@example.com", "...": "..."}, {"id": "cust_def456", "email": "bob@example.com", "...": "..."} ], "pagination": { "page": 2, "pages": 5, "count": 112 } } ``` The current page number. The total number of pages available. The total number of items across all pages. ## Page size All endpoints return **25 items per page**. This is not configurable. ## Iterating through all pages To retrieve all records, increment the `page` parameter until `page` equals `pages`: ```ruby Ruby theme={null} page = 1 all_customers = [] loop do response = api_client.list_customers(page: page) all_customers.concat(response["customers"]) break if page >= response["pagination"]["pages"] page += 1 end ``` ```javascript Node.js theme={null} let page = 1; let allCustomers = []; while (true) { const response = await fetch( `https://api.gettrxn.com/v1/customers?page=${page}`, { headers: { "Authorization": `Bearer ${token}` } } ); const data = await response.json(); allCustomers.push(...data.customers); if (page >= data.pagination.pages) break; page++; } ``` ```python Python theme={null} import requests page = 1 all_customers = [] while True: response = requests.get( f"https://api.gettrxn.com/v1/customers?page={page}", headers={"Authorization": f"Bearer {token}"} ) data = response.json() all_customers.extend(data["customers"]) if page >= data["pagination"]["pages"]: break page += 1 ``` ## Paginated endpoints All list endpoints support pagination: | Endpoint | Resource key | | ------------------------------- | ----------------------- | | `GET /v1/customers` | `customers` | | `GET /v1/products` | `products` | | `GET /v1/prices` | `prices` | | `GET /v1/invoices` | `invoices` | | `GET /v1/subscriptions` | `subscriptions` | | `GET /v1/wallets` | `wallets` | | `GET /v1/crypto_addresses` | `crypto_addresses` | | `GET /v1/crypto_transactions` | `crypto_transactions` | | `GET /v1/crypto_payment_claims` | `crypto_payment_claims` | | `GET /v1/payment_claim_links` | `payment_claim_links` | | `GET /v1/webhook_endpoints` | `webhook_endpoints` | An empty page (no results) still returns a valid pagination object with `count: 0` and `pages: 0`. # Payment claim links Source: https://docs.gettrxn.com/api-reference/payment-claim-links Create and manage payment claim links for customers to submit cryptocurrency payment information. Links support expiration, use limits, and metadata. This API allows you to create and manage payment claim links for customers to submit cryptocurrency payment information. ## The payment claim link object Unique identifier with `pcl_` prefix (e.g., `pcl_abc123def456`). Always `"payment_claim_link"`. Link status: `active`, `used`, or `expired`. When the link expires (ISO 8601). Maximum number of times the link can be used. Number of times the link has been used. When the link was last used (ISO 8601). `null` if never used. Additional notes or metadata. The full URL to share with the customer. The customer this link belongs to. ID of the resulting payment claim. `null` if not used. Whether the link can still be used. Whether the link has expired. When the link was created (ISO 8601). When the link was last updated (ISO 8601). ## List payment claim links ``` GET /v1/payment_claim_links ``` Retrieves a paginated list of all payment claim links in your account. Links are ordered by creation date (newest first). Page number for pagination. Results are returned 25 items per page. ```bash theme={null} curl https://api.gettrxn.com/v1/payment_claim_links \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "payment_claim_links": [ { "id": "pcl_abc123def456", "object": "payment_claim_link", "status": "active", "expires_at": "2025-01-26T12:00:00Z", "max_uses": 1, "use_count": 0, "used_at": null, "metadata": "Payment for Invoice #123", "link": "https://gettrxn.com/payment-claim/RTL1LDMHAdL6JR64ai7frBpphuuNig9q", "customer_id": "cust_xyz789", "crypto_payment_claim_id": null, "usable": true, "expired": false, "created_at": "2025-01-25T12:00:00Z", "updated_at": "2025-01-25T12:00:00Z" } ], "pagination": { "page": 1, "pages": 3, "count": 67 } } ``` ## Get payment claim link ``` GET /v1/payment_claim_links/:id ``` Retrieves a specific payment claim link by ID. The payment claim link's ID (e.g., `pcl_abc123def456`). ```bash theme={null} curl https://api.gettrxn.com/v1/payment_claim_links/pcl_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "pcl_abc123def456", "object": "payment_claim_link", "status": "active", "expires_at": "2025-01-26T12:00:00Z", "max_uses": 1, "use_count": 0, "used_at": null, "metadata": "Payment for Invoice #123", "link": "https://gettrxn.com/payment-claim/RTL1LDMHAdL6JR64ai7frBpphuuNig9q", "customer_id": "cust_xyz789", "crypto_payment_claim_id": null, "usable": true, "expired": false, "created_at": "2025-01-25T12:00:00Z", "updated_at": "2025-01-25T12:00:00Z" } ``` ## Create payment claim link ``` POST /v1/payment_claim_links ``` Creates a new payment claim link for a customer. The customer's ID (e.g., `cust_abc123def456`). Must belong to your account. When the link expires (ISO 8601 format). Defaults to 24 hours from now. Maximum number of times the link can be used. Must be greater than 0. Additional notes or metadata for the link. ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/payment_claim_links \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cust_abc123def456", "expires_at": "2025-01-26T10:00:00Z", "max_uses": 1, "metadata": "Payment for Invoice #123" }' ``` ```json Response theme={null} { "id": "pcl_xyz789abc123", "object": "payment_claim_link", "status": "active", "expires_at": "2025-01-26T10:00:00Z", "max_uses": 1, "use_count": 0, "used_at": null, "metadata": "Payment for Invoice #123", "link": "https://gettrxn.com/payment-claim/RTL1LDMHAdL6JR64ai7frBpphuuNig9q", "customer_id": "cust_abc123def456", "crypto_payment_claim_id": null, "usable": true, "expired": false, "created_at": "2025-01-25T14:30:00Z", "updated_at": "2025-01-25T14:30:00Z" } ``` ### Error responses ```json Customer not found theme={null} { "error": { "type": "invalid_request_error", "code": "resource_not_found", "message": "Customer not found", "param": "customer_id" } } ``` ```json Invalid max_uses theme={null} { "error": { "type": "invalid_request_error", "code": "parameter_invalid", "message": "Max uses must be greater than 0", "param": "max_uses" } } ``` ## Delete payment claim link ``` DELETE /v1/payment_claim_links/:id ``` Deletes a payment claim link. The payment claim link's ID (e.g., `pcl_xyz789abc123`). ```bash theme={null} curl -X DELETE https://api.gettrxn.com/v1/payment_claim_links/pcl_xyz789abc123 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` Returns `204 No Content` on successful deletion. ## Link statuses | Status | Description | | --------- | -------------------------------------- | | `active` | Link is active and can be used. | | `used` | Link has reached its `max_uses` limit. | | `expired` | Link has expired (past `expires_at`). | ## Checking link usability The `usable` field indicates whether a link can still be used. A link is usable if: * Status is `active`. * Not expired (`expires_at` is in the future). * Use count is less than `max_uses`. ## Usage flow 1. **Create a payment claim link** for a customer using their ID. 2. **Share the link** with the customer (via email, SMS, etc.). 3. **Customer visits the link** and submits their payment information. 4. **Review the submission** in your dashboard under Crypto Payment Claims. 5. **Approve or reject** the payment claim. 6. **Optionally delete the link** if no longer needed. ## Sandbox support The API respects sandbox scoping. If your API token is associated with a sandbox, you can only access links created in that sandbox. Links created in sandbox mode will be isolated from production data. # Payment claims Source: https://docs.gettrxn.com/api-reference/payment-claims View, approve, and reject cryptocurrency payment claims submitted by customers. Payment claims go through a review workflow before transactions are created. This API allows you to view, approve, and reject cryptocurrency payment claims submitted by customers. ## The payment claim object Unique identifier with `pymt_claim_` prefix (e.g., `pymt_claim_abc123def456`). Always `"payment_claim"`. Claim status: `pending`, `approved`, or `rejected`. Claimed transaction amount. Blockchain transaction hash provided by customer. Reason for rejection. `null` if not rejected. When the claim was approved or rejected (ISO 8601). `null` if pending. The customer who submitted the claim. Currency code (e.g., `BTC`, `ETH`). The receiving address ID. The receiving address. User ID who approved or rejected. `null` if pending. Created transaction ID. `null` if not approved. When the claim was submitted (ISO 8601). When the claim was last updated (ISO 8601). ## List payment claims ``` GET /v1/crypto_payment_claims ``` Retrieves a paginated list of all payment claims in your account. Claims are ordered by creation date (newest first). Page number for pagination. Results are returned 25 items per page. Filter by status. One of: `pending`, `approved`, `rejected`. ```bash theme={null} curl https://api.gettrxn.com/v1/crypto_payment_claims \ -H "Authorization: Bearer $TRXN_TOKEN" ``` #### Filtering by status ```bash theme={null} curl "https://api.gettrxn.com/v1/crypto_payment_claims?status=pending" \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "crypto_payment_claims": [ { "id": "pymt_claim_abc123def456", "object": "payment_claim", "status": "pending", "amount": "0.5", "transaction_id": "0x1234567890abcdef...", "rejection_reason": null, "approved_at": null, "customer_id": "cust_xyz789", "currency": "BTC", "to_address_id": "addr_abc123", "to_address": "bc1q...", "approved_by_id": null, "crypto_transaction_id": null, "created_at": "2025-01-25T12:00:00Z", "updated_at": "2025-01-25T12:00:00Z" } ], "pagination": { "page": 1, "pages": 3, "count": 67 } } ``` ## Get payment claim ``` GET /v1/crypto_payment_claims/:id ``` Retrieves a specific payment claim by ID. The payment claim's ID (e.g., `pymt_claim_abc123def456`). ```bash theme={null} curl https://api.gettrxn.com/v1/crypto_payment_claims/pymt_claim_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "pymt_claim_abc123def456", "object": "payment_claim", "status": "pending", "amount": "0.5", "transaction_id": "0x1234567890abcdef...", "rejection_reason": null, "approved_at": null, "customer_id": "cust_xyz789", "currency": "BTC", "to_address_id": "addr_abc123", "to_address": "bc1q...", "approved_by_id": null, "crypto_transaction_id": null, "created_at": "2025-01-25T12:00:00Z", "updated_at": "2025-01-25T12:00:00Z" } ``` ## Approve payment claim ``` POST /v1/crypto_payment_claims/:crypto_payment_claim_id/approval ``` Approves a pending payment claim. This creates a corresponding crypto transaction record. The payment claim's ID (e.g., `pymt_claim_abc123def456`). ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/crypto_payment_claims/pymt_claim_abc123def456/approval \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "pymt_claim_abc123def456", "object": "payment_claim", "status": "approved", "amount": "0.5", "transaction_id": "0x1234567890abcdef...", "rejection_reason": null, "approved_at": "2025-01-25T14:30:00Z", "customer_id": "cust_xyz789", "currency": "BTC", "to_address_id": "addr_abc123", "to_address": "bc1q...", "approved_by_id": 123, "crypto_transaction_id": "trxn_new789xyz", "created_at": "2025-01-25T12:00:00Z", "updated_at": "2025-01-25T14:30:00Z" } ``` ### Error when claim is not pending ```json theme={null} { "error": { "type": "invalid_request_error", "code": "parameter_invalid", "message": "Payment claim is not pending", "param": "status" } } ``` ## Reject payment claim ``` POST /v1/crypto_payment_claims/:crypto_payment_claim_id/rejection ``` Rejects a pending payment claim with an optional reason. The payment claim's ID (e.g., `pymt_claim_abc123def456`). Reason for rejection. Defaults to `"No reason provided"` if omitted. ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/crypto_payment_claims/pymt_claim_abc123def456/rejection \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "reason": "Transaction not found on blockchain" }' ``` ```json Response theme={null} { "id": "pymt_claim_abc123def456", "object": "payment_claim", "status": "rejected", "amount": "0.5", "transaction_id": "0x1234567890abcdef...", "rejection_reason": "Transaction not found on blockchain", "approved_at": "2025-01-25T14:30:00Z", "customer_id": "cust_xyz789", "currency": "BTC", "to_address_id": "addr_abc123", "to_address": "bc1q...", "approved_by_id": 123, "crypto_transaction_id": null, "created_at": "2025-01-25T12:00:00Z", "updated_at": "2025-01-25T14:30:00Z" } ``` ## Payment claim statuses | Status | Description | | ---------- | ----------------------------------------------- | | `pending` | Awaiting review -- can be approved or rejected. | | `approved` | Payment verified and transaction created. | | `rejected` | Payment claim was rejected. | ## Workflow 1. Customer submits a payment claim via a payment claim link. 2. Claim appears with `pending` status. 3. Review the claim by checking the transaction on the blockchain. 4. Approve the claim (creates a crypto transaction) or reject it with a reason. 5. Approved claims can be allocated to invoices. ## Webhooks The following webhook events are triggered for payment claims: | Event | Description | | ------------------------- | ------------------------------ | | `payment_claim.submitted` | When a new claim is submitted. | | `payment_claim.approved` | When a claim is approved. | | `payment_claim.rejected` | When a claim is rejected. | ## Sandbox support The API respects sandbox scoping. If your API token is associated with a sandbox, you can only access claims created in that sandbox. Approved and rejected claims are isolated from production data. # Prices Source: https://docs.gettrxn.com/api-reference/prices Create and manage prices for your products. Prices define the cost and billing structure (one-time or recurring) for your products. This API allows you to create and manage prices for your products. Prices define the cost and billing structure (one-time or recurring) for your products, enabling customers to make cryptocurrency payments. ## The price object Unique identifier with `price_` prefix (e.g., `price_abc123def456`). The price amount in cents (e.g., `2999` for \$29.99). The associated product's ID. Whether the price is active. Defaults to `true` on creation. Recurring billing configuration. `null` for one-time prices. Billing interval: `day`, `week`, `month`, or `year`. Number of intervals between charges. When the price was created (ISO 8601). When the price was last updated (ISO 8601). ### One-time price example ```json theme={null} { "id": "price_def456ghi789", "amount": 4999, "product_id": "prod_xyz789ghi012", "active": true, "recurring": null, "created_at": "2024-01-02T10:30:00Z", "updated_at": "2024-01-02T10:30:00Z" } ``` ### Recurring price example ```json theme={null} { "id": "price_abc123def456", "amount": 2999, "product_id": "prod_xyz789ghi012", "active": true, "recurring": { "interval": "month", "interval_count": 1 }, "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T12:00:00Z" } ``` ## List prices ``` GET /v1/prices ``` Retrieves a paginated list of all prices in your account. Page number for pagination. Results are returned 25 items per page. ```bash theme={null} curl https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "prices": [ { "id": "price_abc123def456", "amount": 2999, "product_id": "prod_xyz789ghi012", "active": true, "recurring": { "interval": "month", "interval_count": 1 }, "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T12:00:00Z" }, { "id": "price_def456ghi789", "amount": 4999, "product_id": "prod_xyz789ghi012", "active": true, "recurring": null, "created_at": "2024-01-02T10:30:00Z", "updated_at": "2024-01-02T10:30:00Z" } ], "pagination": { "page": 1, "pages": 3, "count": 67 } } ``` ## Get price ``` GET /v1/prices/:id ``` Retrieves a specific price by ID. The price's ID (e.g., `price_abc123def456`). ```bash theme={null} curl https://api.gettrxn.com/v1/prices/price_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response (recurring) theme={null} { "id": "price_abc123def456", "amount": 2999, "product_id": "prod_xyz789ghi012", "active": true, "recurring": { "interval": "month", "interval_count": 1 }, "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T12:00:00Z" } ``` ```json Response (one-time) theme={null} { "id": "price_def456ghi789", "amount": 4999, "product_id": "prod_xyz789ghi012", "active": true, "recurring": null, "created_at": "2024-01-02T10:30:00Z", "updated_at": "2024-01-02T10:30:00Z" } ``` ## Create price ``` POST /v1/prices ``` Creates a new price for a product in your account. The product's ID (e.g., `prod_abc123def456`). Must belong to your account. The price amount in cents (e.g., `2999` for \$29.99). Must be 0 or greater. Object containing recurring billing configuration. Omit for one-time prices. Billing interval. One of: `day`, `week`, `month`, `year`. Number of intervals between charges. Must be a positive integer (e.g., `1` for monthly, `3` for quarterly). ### Create a one-time price ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_abc123def456", "amount": 4999 }' ``` ### Create a monthly recurring price ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_abc123def456", "amount": 2999, "recurring": { "interval": "month", "interval_count": 1 } }' ``` ### Create a quarterly recurring price ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_abc123def456", "amount": 9999, "recurring": { "interval": "month", "interval_count": 3 } }' ``` ```json Response theme={null} { "id": "price_new123price456", "amount": 2999, "product_id": "prod_abc123def456", "active": true, "recurring": { "interval": "month", "interval_count": 1 }, "created_at": "2024-01-15T14:30:00Z", "updated_at": "2024-01-15T14:30:00Z" } ``` ### Validation errors ```json Amount is required theme={null} { "errors": { "amount": ["can't be blank"] } } ``` ```json Amount must be non-negative theme={null} { "errors": { "amount": ["must be greater than or equal to 0"] } } ``` ```json Interval is required for recurring prices theme={null} { "errors": { "interval": ["is required for recurring prices. Valid options are: \"day\", \"week\", \"month\", or \"year\""] } } ``` ```json Interval count is required theme={null} { "errors": { "interval_count": ["is required for recurring prices"] } } ``` ```json Interval count must be a positive integer theme={null} { "errors": { "interval_count": ["must be greater than zero and only integer values are valid"] } } ``` ## Update price ``` PATCH /v1/prices/:id ``` Updates a price's active status. Only the `active` attribute can be modified after creation. The price's ID (e.g., `price_abc123def456`). Whether the price is active. Set to `false` to deactivate. ```bash theme={null} curl -X PATCH https://api.gettrxn.com/v1/prices/price_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "active": false }' ``` ```json Response theme={null} { "id": "price_abc123def456", "amount": 2999, "product_id": "prod_xyz789ghi012", "active": false, "recurring": { "interval": "month", "interval_count": 1 }, "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-15T16:45:00Z" } ``` ## Delete price ``` DELETE /v1/prices/:id ``` Deletes a price from your account. This action is irreversible and will also delete associated subscriptions and line items. The price's ID (e.g., `price_abc123def456`). ```bash theme={null} curl -X DELETE https://api.gettrxn.com/v1/prices/price_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` Returns `204 No Content` on successful deletion. ## Recurring billing intervals Prices can be configured for recurring billing with flexible intervals: | Configuration | interval | interval\_count | | ------------- | -------- | --------------- | | Daily | `day` | `1` | | Weekly | `week` | `1` | | Bi-weekly | `week` | `2` | | Monthly | `month` | `1` | | Quarterly | `month` | `3` | | Semi-annually | `month` | `6` | | Yearly | `year` | `1` | ## Recurring billing examples ```bash Weekly subscription ($9.99/week) theme={null} curl -X POST https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_abc123def456", "amount": 999, "recurring": { "interval": "week", "interval_count": 1 } }' ``` ```bash Quarterly subscription ($49.99 every 3 months) theme={null} curl -X POST https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_abc123def456", "amount": 4999, "recurring": { "interval": "month", "interval_count": 3 } }' ``` ```bash Annual subscription ($99.99/year) theme={null} curl -X POST https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_abc123def456", "amount": 9999, "recurring": { "interval": "year", "interval_count": 1 } }' ``` ## Integration example ```bash theme={null} # 1. Create a product curl -X POST https://api.gettrxn.com/v1/products \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "Premium Course"}' # Response: {"id": "prod_abc123def456", "name": "Premium Course", ...} # 2. Create a one-time price curl -X POST https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_abc123def456", "amount": 19999 }' # Response: {"id": "price_onetime123", "amount": 19999, "recurring": null, ...} # 3. Create a monthly subscription price curl -X POST https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_abc123def456", "amount": 2999, "recurring": { "interval": "month", "interval_count": 1 } }' # Response: {"id": "price_monthly123", "amount": 2999, "recurring": {...}, ...} ``` ## Sandbox support The API respects sandbox scoping. If your API token is associated with a sandbox, you can only access prices for products created in that sandbox. # Products Source: https://docs.gettrxn.com/api-reference/products Create and manage products for your account. Products represent the items or services that your customers can purchase using cryptocurrency payments. This API allows you to create and manage products for your account. Products represent the items or services that your customers can purchase using cryptocurrency payments. ## The product object Unique identifier with `prod_` prefix (e.g., `prod_abc123def456`). The product's name. Leading and trailing whitespace is automatically trimmed. When the product was created (ISO 8601). When the product was last updated (ISO 8601). ## List products ``` GET /v1/products ``` Retrieves a paginated list of all products in your account. Page number for pagination. Results are returned 25 items per page. ```bash theme={null} curl https://api.gettrxn.com/v1/products \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "products": [ { "id": "prod_abc123def456", "name": "Premium Subscription", "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T12:00:00Z" }, { "id": "prod_xyz789ghi012", "name": "Digital Course", "created_at": "2024-01-02T10:30:00Z", "updated_at": "2024-01-02T10:30:00Z" } ], "pagination": { "page": 1, "pages": 3, "count": 67 } } ``` ## Get product ``` GET /v1/products/:id ``` Retrieves a specific product by ID. The product's ID (e.g., `prod_abc123def456`). ```bash theme={null} curl https://api.gettrxn.com/v1/products/prod_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "prod_abc123def456", "name": "Premium Subscription", "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T12:00:00Z" } ``` ## Create product ``` POST /v1/products ``` Creates a new product in your account. The product's name. Whitespace is automatically trimmed. Cannot be blank. ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/products \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "New Digital Product" }' ``` ```json Response theme={null} { "id": "prod_new123product456", "name": "New Digital Product", "created_at": "2024-01-15T14:30:00Z", "updated_at": "2024-01-15T14:30:00Z" } ``` ### Validation errors ```json Name cannot be blank theme={null} { "errors": { "name": ["can't be blank"] } } ``` ## Update product ``` PATCH /v1/products/:id ``` Updates an existing product. The product's ID (e.g., `prod_abc123def456`). The product's name. Whitespace is automatically trimmed. ```bash theme={null} curl -X PATCH https://api.gettrxn.com/v1/products/prod_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Product Name" }' ``` ```json Response theme={null} { "id": "prod_abc123def456", "name": "Updated Product Name", "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-15T16:45:00Z" } ``` ## Delete product ``` DELETE /v1/products/:id ``` Deletes a product from your account. This action is irreversible and will also delete the product's associated prices and other related data. The product's ID (e.g., `prod_abc123def456`). ```bash theme={null} curl -X DELETE https://api.gettrxn.com/v1/products/prod_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` Returns `204 No Content` on successful deletion. ## Product features ### Automatic name trimming Product names automatically have leading and trailing whitespace removed during validation. This ensures consistent formatting and prevents issues with extra spaces. ### Associated prices Products can have multiple associated prices for different pricing models (one-time payments, subscriptions, etc.). When a product is deleted, all associated prices are automatically removed. ### Name validation Product names have the following validation rules: * **Required**: Every product must have a name. * **Trimmed**: Leading and trailing whitespace is automatically removed. * **Flexible**: Names can contain special characters, unicode, and emojis. * **No uniqueness constraint**: Multiple products can have the same name within an account. ## Pagination By default, the API returns 25 products per page. Use the `page` query parameter to navigate through pages. ## Integration example ```bash theme={null} # 1. Create a product curl -X POST https://api.gettrxn.com/v1/products \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "Premium Course"}' # Response: {"id": "prod_abc123def456", "name": "Premium Course", ...} # 2. Create a price for the product curl -X POST https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_abc123def456", "amount": 9999, "currency": "USD", "recurring": false }' # 3. Create a payment claim link using the price curl -X POST https://api.gettrxn.com/v1/payment_claim_links \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"price_id": "price_xyz789"}' ``` ## Sandbox support The API respects sandbox scoping. If your API token is associated with a sandbox, you can only access products created in that sandbox. Product names can be duplicated across different accounts and sandboxes. # Subscriptions Source: https://docs.gettrxn.com/api-reference/subscriptions Create and manage subscriptions for recurring billing. Subscriptions support multiple phases for trials, promotional periods, and plan changes. This API allows you to create and manage subscriptions for your account. Subscriptions represent recurring billing arrangements with customers, supporting multiple phases for complex billing scenarios like trials, promotional periods, and plan changes. ## The subscription object Unique identifier with `sub_` prefix (e.g., `sub_abc123def456`). The customer's ID associated with this subscription. How proration is handled: `create_prorations`, `always_invoice`, or `none`. The anchor date for billing cycles (ISO 8601). When the subscription started (ISO 8601). When the subscription ends. `null` for ongoing subscriptions. When the next invoice will be generated (ISO 8601). The total recurring amount for the subscription. Array of subscription phases. Phase start date (ISO 8601). Phase end date (ISO 8601). `null` for ongoing phases. Whether this phase is currently active. Array of items in this phase. The price ID for this item. The product ID. The product name. Item quantity. Price per unit. Overridden price amount. `null` if using the standard price. The actual price used (overridden or standard). Whether this item recurs. Billing interval (e.g., `month`). Number of intervals between charges. When the subscription was created (ISO 8601). When the subscription was last updated (ISO 8601). ## List subscriptions ``` GET /v1/subscriptions ``` Retrieves a paginated list of all subscriptions in your account. Page number for pagination. Results are returned 25 items per page. ```bash theme={null} curl https://api.gettrxn.com/v1/subscriptions \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "subscriptions": [ { "id": "sub_abc123def456", "customer_id": "cus_xyz789", "proration_behavior": "create_prorations", "billing_cycle_anchor": "2025-02-01T00:00:00Z", "start_date": "2025-01-15T00:00:00Z", "end_date": null, "next_billing_date": "2025-02-15T00:00:00Z", "total_amount": "99.99", "created_at": "2025-01-15T12:00:00Z", "updated_at": "2025-01-15T12:00:00Z", "phases": [ { "start_date": "2025-01-15T00:00:00Z", "end_date": null, "is_active": true, "items": [ { "price_id": "pri_abc123", "product_id": "pro_xyz789", "product_name": "Premium Plan", "quantity": 1, "unit_amount": "99.99", "overridden_price_amount": null, "effective_price": "99.99", "recurring": true, "interval": "month", "interval_count": 1 } ] } ] } ], "pagination": { "page": 1, "pages": 2, "count": 45 } } ``` ## Get subscription ``` GET /v1/subscriptions/:id ``` Retrieves a specific subscription by ID with all phases and items. The subscription's ID (e.g., `sub_abc123def456`). ```bash theme={null} curl https://api.gettrxn.com/v1/subscriptions/sub_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "sub_abc123def456", "customer_id": "cus_xyz789", "proration_behavior": "create_prorations", "billing_cycle_anchor": "2025-02-01T00:00:00Z", "start_date": "2025-01-15T00:00:00Z", "end_date": null, "next_billing_date": "2025-02-15T00:00:00Z", "total_amount": "99.99", "created_at": "2025-01-15T12:00:00Z", "updated_at": "2025-01-15T12:00:00Z", "phases": [ { "start_date": "2025-01-15T00:00:00Z", "end_date": null, "is_active": true, "items": [ { "price_id": "pri_abc123", "product_id": "pro_xyz789", "product_name": "Premium Plan", "quantity": 1, "unit_amount": "99.99", "overridden_price_amount": null, "effective_price": "99.99", "recurring": true, "interval": "month", "interval_count": 1 } ] } ] } ``` ## Create subscription ``` POST /v1/subscriptions ``` Creates a new subscription with phases and items. The customer's ID. Must belong to your account. How to handle proration. One of: `create_prorations`, `always_invoice`, `none`. The anchor date for billing cycles (ISO 8601 format). Array of phase objects. Must include at least one phase. Phase start date (format: `YYYY-MM-DD` or ISO 8601). Phase end date. `null` or omit for ongoing phases. Array of phase item objects. The price ID for the item. Must belong to your account. Quantity for this item. Override the price amount for this item. ### Simple subscription ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/subscriptions \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cus_xyz789", "billing_cycle_anchor": "2025-02-01T00:00:00Z", "proration_behavior": "create_prorations", "phases": [ { "start_date": "2025-01-15", "items": [ {"price_id": "pri_monthly_plan", "quantity": 1} ] } ] }' ``` ```json Response theme={null} { "id": "sub_new123subscription456", "customer_id": "cus_xyz789", "proration_behavior": "create_prorations", "billing_cycle_anchor": "2025-02-01T00:00:00Z", "start_date": "2025-01-15T00:00:00Z", "end_date": null, "next_billing_date": "2025-02-01T00:00:00Z", "total_amount": "99.99", "created_at": "2025-01-15T14:30:00Z", "updated_at": "2025-01-15T14:30:00Z", "phases": [...] } ``` ### Multi-phase subscription (trial then paid) Create a subscription with a trial phase followed by a paid phase: ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/subscriptions \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cus_xyz789", "phases": [ { "start_date": "2025-01-15", "end_date": "2025-02-15", "items": [ {"price_id": "pri_trial_plan", "quantity": 1} ] }, { "start_date": "2025-02-15", "items": [ {"price_id": "pri_monthly_plan", "quantity": 1} ] } ] }' ``` ### Error responses ```json Customer not found theme={null} { "error": "Customer not found" } ``` ```json Price not found theme={null} { "error": "Price not found: pri_invalid123" } ``` ```json Missing phases theme={null} { "errors": { "subscription_phases": ["must have at least one phase"] } } ``` ## Update subscription ``` PATCH /v1/subscriptions/:id ``` Updates an existing subscription's proration behavior or billing cycle anchor. The subscription's ID. How to handle proration. One of: `create_prorations`, `always_invoice`, `none`. The anchor date for billing cycles (ISO 8601 format). ```bash theme={null} curl -X PATCH https://api.gettrxn.com/v1/subscriptions/sub_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "proration_behavior": "none" }' ``` ```json Response theme={null} { "id": "sub_abc123def456", "customer_id": "cus_xyz789", "proration_behavior": "none", "billing_cycle_anchor": "2025-02-01T00:00:00Z", "start_date": "2025-01-15T00:00:00Z", "end_date": null, "next_billing_date": "2025-02-15T00:00:00Z", "total_amount": "99.99", "created_at": "2025-01-15T12:00:00Z", "updated_at": "2025-01-16T10:00:00Z", "phases": [...] } ``` ## Delete subscription ``` DELETE /v1/subscriptions/:id ``` Cancels and deletes a subscription. The subscription's ID. ```bash theme={null} curl -X DELETE https://api.gettrxn.com/v1/subscriptions/sub_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` Returns `204 No Content` on successful deletion. ## Proration behavior values | Value | Description | | ------------------- | -------------------------------------------------------------------------------------- | | `create_prorations` | Create prorated charges/credits when subscription changes mid-billing cycle (default). | | `always_invoice` | Always create a full invoice immediately regardless of billing cycle. | | `none` | No proration -- charges start on the next billing cycle. | ## Subscription phases Subscriptions support multiple phases to handle complex billing scenarios: * **Trial phases**: Free or discounted periods at the start of a subscription. * **Promotional phases**: Temporary discounts or special pricing. * **Plan changes**: Scheduled upgrades or downgrades. * **Ongoing phases**: Indefinite billing with no end date. ### Phase rules All recurring items within a phase must have the same interval (e.g., all monthly). Phases cannot overlap and must be sequential without gaps. 1. Phases must have at least one item. 2. All recurring items within a phase must have the same interval. 3. Phases cannot overlap. 4. Phases must be sequential without gaps (if an end date is specified). 5. Only one phase can be active at any time. ## Billing cycle anchor The `billing_cycle_anchor` determines when recurring invoices are generated: * If set, invoices are generated relative to this date. * If not set, invoices are generated relative to the subscription start date. * Useful for aligning billing to specific dates (e.g., 1st of the month). ## Automatic invoice generation When a subscription is created: 1. An initial invoice may be generated based on proration behavior. 2. Recurring invoices are automatically generated on billing dates. 3. The `next_billing_date` field shows when the next invoice will be created. ## Integration example ```bash theme={null} # 1. Create a customer curl -X POST https://api.gettrxn.com/v1/customers \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"email": "subscriber@example.com"}' # Response: {"id": "cus_abc123", ...} # 2. Create a subscription with a trial and regular phase curl -X POST https://api.gettrxn.com/v1/subscriptions \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cus_abc123", "phases": [ { "start_date": "2025-01-15", "end_date": "2025-01-29", "items": [ {"price_id": "pri_free_trial", "quantity": 1} ] }, { "start_date": "2025-01-29", "items": [ {"price_id": "pri_monthly_plan", "quantity": 1} ] } ] }' # 3. Check subscription status curl https://api.gettrxn.com/v1/subscriptions/sub_xyz789 \ -H "Authorization: Bearer $TRXN_TOKEN" # 4. Update proration behavior if needed curl -X PATCH https://api.gettrxn.com/v1/subscriptions/sub_xyz789 \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"proration_behavior": "none"}' ``` ## Sandbox support The API respects sandbox scoping. If your API token is associated with a sandbox, you can only access subscriptions created in that sandbox. Subscriptions created in sandbox mode will be isolated from production data. # Wallets Source: https://docs.gettrxn.com/api-reference/wallets View cryptocurrency wallets in your account. Wallets can belong to either your account or to individual customers. This API allows you to view cryptocurrency wallets in your account. Wallets can belong to either your account or to individual customers. ## The wallet object Unique identifier with `wall_` prefix (e.g., `wall_abc123def456`). Always `"wallet"`. Type of owner: `account` or `customer`. ID of the owning account or customer. List of crypto addresses in this wallet. Unique identifier with `addr_` prefix. The cryptocurrency address. Currency code (e.g., `BTC`, `ETH`). When the address was created (ISO 8601). Number of addresses in the wallet. When the wallet was created (ISO 8601). When the wallet was last updated (ISO 8601). ## List wallets ``` GET /v1/wallets ``` Retrieves a paginated list of all wallets in your account, including both account wallets and customer wallets. Wallets are ordered by creation date (newest first). Page number for pagination. Results are returned 25 items per page. ```bash theme={null} curl https://api.gettrxn.com/v1/wallets \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "wallets": [ { "id": "wall_abc123def456", "object": "wallet", "owner_type": "account", "owner_id": "acct_xyz789", "crypto_addresses": [ { "id": "addr_123", "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "currency": "BTC", "created_at": "2025-01-25T12:00:00Z" }, { "id": "addr_456", "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f...", "currency": "ETH", "created_at": "2025-01-25T12:30:00Z" } ], "crypto_address_count": 2, "created_at": "2025-01-25T10:00:00Z", "updated_at": "2025-01-25T12:30:00Z" }, { "id": "wall_ghi789jkl012", "object": "wallet", "owner_type": "customer", "owner_id": "cust_abc123", "crypto_addresses": [ { "id": "addr_789", "address": "bc1q...", "currency": "BTC", "created_at": "2025-01-25T14:00:00Z" } ], "crypto_address_count": 1, "created_at": "2025-01-25T14:00:00Z", "updated_at": "2025-01-25T14:00:00Z" } ], "pagination": { "page": 1, "pages": 1, "count": 2 } } ``` ## Get wallet ``` GET /v1/wallets/:id ``` Retrieves a specific wallet by ID, including all its crypto addresses. The wallet's ID (e.g., `wall_abc123def456`). ```bash theme={null} curl https://api.gettrxn.com/v1/wallets/wall_abc123def456 \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "wall_abc123def456", "object": "wallet", "owner_type": "account", "owner_id": "acct_xyz789", "crypto_addresses": [ { "id": "addr_123", "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "currency": "BTC", "created_at": "2025-01-25T12:00:00Z" }, { "id": "addr_456", "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f...", "currency": "ETH", "created_at": "2025-01-25T12:30:00Z" } ], "crypto_address_count": 2, "created_at": "2025-01-25T10:00:00Z", "updated_at": "2025-01-25T12:30:00Z" } ``` ## Wallet types ### Account wallets Account wallets belong to your main account and are used for receiving payments directly to your business. * `owner_type`: `"account"` * `owner_id`: Your account ID (e.g., `acct_xyz789`) ### Customer wallets Customer wallets are automatically created for each customer and are used for customer-specific payment tracking. * `owner_type`: `"customer"` * `owner_id`: The customer's ID (e.g., `cust_abc123`) ## Automatic wallet creation Account wallets are created automatically when your account is created. Customer wallets are created automatically when a customer is created. You do not need to create wallets manually. ## Adding addresses to wallets Use the [Crypto Addresses API](/api-reference/crypto-addresses) to add addresses to a wallet: ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/crypto_addresses \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "wallet_id": "wall_abc123def456", "currency_code": "BTC", "address": "bc1q..." }' ``` ## Sandbox support The API respects sandbox scoping. If your API token is associated with a sandbox, you can only access wallets in that sandbox. Customer wallets for sandboxed customers are automatically sandboxed. Wallet data is isolated from production data. # Webhook endpoints Source: https://docs.gettrxn.com/api-reference/webhook-endpoints Manage webhook endpoints that receive real-time event notifications. Includes HMAC-SHA256 signature verification, event types, and retry behavior. The Webhooks API allows you to manage webhook endpoints that receive real-time event notifications when activities occur in your account. ## List webhook endpoints ``` GET /v1/webhook_endpoints ``` Returns a paginated list of webhook endpoints for the current account. Page number for pagination. ```bash theme={null} curl https://api.gettrxn.com/v1/webhook_endpoints \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "webhook_endpoints": [ { "id": "we_xxx", "url": "https://example.com/webhook", "description": "Production webhook", "enabled": true, "subscriptions": ["*"], "created_at": "2025-01-14T12:00:00Z", "updated_at": "2025-01-14T12:00:00Z", "signing_secret_last4": "a1b2" } ], "pagination": { "page": 1, "pages": 1, "count": 1 } } ``` ## Get webhook endpoint ``` GET /v1/webhook_endpoints/:id ``` Returns details of a specific webhook endpoint. The webhook endpoint's ID (e.g., `we_xxx`). ```bash theme={null} curl https://api.gettrxn.com/v1/webhook_endpoints/we_xxx \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ```json Response theme={null} { "id": "we_xxx", "url": "https://example.com/webhook", "description": "Production webhook", "enabled": true, "subscriptions": ["invoice.created", "invoice.paid"], "created_at": "2025-01-14T12:00:00Z", "updated_at": "2025-01-14T12:00:00Z", "signing_secret_last4": "a1b2" } ``` ## Create webhook endpoint ``` POST /v1/webhook_endpoints ``` Creates a new webhook endpoint. The URL where webhook events will be sent via HTTP POST. Must use HTTPS. Event types to subscribe to. Use `["*"]` for all events, or specify individual events like `["invoice.created", "invoice.paid"]`. Whether the endpoint is active. Optional description for the endpoint. ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/webhook_endpoints \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/webhook", "subscriptions": ["invoice.created", "invoice.paid"], "enabled": true, "description": "Invoice notifications" }' ``` The signing secret is only returned in full once -- at creation time. Store it securely as you will need it to verify webhook signatures. The response includes the full `signing_secret` (returned as `201 Created`). ## Update webhook endpoint ``` PUT /v1/webhook_endpoints/:id ``` Updates an existing webhook endpoint. The webhook endpoint's ID. The endpoint URL. Event types to subscribe to. Whether the endpoint is active. Optional description. ```bash theme={null} curl -X PUT https://api.gettrxn.com/v1/webhook_endpoints/we_xxx \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "enabled": false }' ``` Returns the updated webhook endpoint. ## Delete webhook endpoint ``` DELETE /v1/webhook_endpoints/:id ``` Deletes a webhook endpoint. This also deletes all associated webhook events. The webhook endpoint's ID. ```bash theme={null} curl -X DELETE https://api.gettrxn.com/v1/webhook_endpoints/we_xxx \ -H "Authorization: Bearer $TRXN_TOKEN" ``` Returns `204 No Content`. ## Regenerate signing secret ``` POST /v1/webhook_endpoints/:id/regenerate_secret ``` Regenerates the signing secret for a webhook endpoint. The previous secret is immediately invalidated. The webhook endpoint's ID. ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/webhook_endpoints/we_xxx/regenerate_secret \ -H "Authorization: Bearer $TRXN_TOKEN" ``` Returns the webhook endpoint with the new full `signing_secret`. After regenerating the secret, you must update your integration to use the new secret. Webhooks signed with the old secret will fail verification. ## Webhook security TRXN signs all webhook requests with HMAC-SHA256 signatures, allowing you to verify that webhooks are genuinely from TRXN. ### Signature header format Every webhook request includes an `X-Trxn-Signature` header: ``` X-Trxn-Signature: t=,v1= ``` Example: ``` X-Trxn-Signature: t=1704067200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd ``` ### Signing secret format Signing secrets use the format `whsec_<64_hex_chars>`: ``` whsec_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2 ``` ### Verifying signatures To verify a webhook signature: 1. Extract the timestamp (`t`) and signature (`v1`) from the header. 2. Check the timestamp is within 5 minutes of current time (prevents replay attacks). 3. Compute the expected signature: `HMAC-SHA256(secret, ".")`. 4. Compare signatures using constant-time comparison. Use the full signing secret (including the `whsec_` prefix) as the HMAC key. This matches Stripe's implementation. ```ruby Ruby theme={null} def verify_webhook(payload, signature_header, secret) parts = signature_header.split(",").to_h { |p| p.split("=", 2) } timestamp = parts["t"].to_i signature = parts["v1"] # Check timestamp is within 5 minutes return false if (Time.now.to_i - timestamp).abs > 300 # Compute expected signature (use full secret including whsec_ prefix) signed_payload = "#{timestamp}.#{payload}" expected = OpenSSL::HMAC.hexdigest("SHA256", secret, signed_payload) # Constant-time comparison ActiveSupport::SecurityUtils.secure_compare(expected, signature) end ``` ```javascript Node.js theme={null} const crypto = require('crypto'); function verifyWebhook(payload, signatureHeader, secret) { const parts = Object.fromEntries( signatureHeader.split(',').map(p => p.split('=')) ); const timestamp = parseInt(parts.t, 10); const signature = parts.v1; // Check timestamp is within 5 minutes if (Math.abs(Date.now() / 1000 - timestamp) > 300) { return false; } // Compute expected signature (use full secret including whsec_ prefix) const signedPayload = `${timestamp}.${payload}`; const expected = crypto .createHmac('sha256', secret) .update(signedPayload) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(signature) ); } ``` ```python Python theme={null} import hmac import hashlib import time def verify_webhook(payload: str, signature_header: str, secret: str) -> bool: parts = dict(p.split("=", 1) for p in signature_header.split(",")) timestamp = int(parts["t"]) signature = parts["v1"] # Check timestamp is within 5 minutes if abs(time.time() - timestamp) > 300: return False # Compute expected signature (use full secret including whsec_ prefix) signed_payload = f"{timestamp}.{payload}" expected = hmac.new( secret.encode(), signed_payload.encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature) ``` ## Available events | Event | Description | | ------------------ | -------------------------------- | | `customer.created` | A new customer has been created. | | `customer.updated` | A customer has been updated. | | `customer.deleted` | A customer has been deleted. | | Event | Description | | ----------------- | ------------------------------- | | `product.created` | A new product has been created. | | `product.updated` | A product has been updated. | | `product.deleted` | A product has been deleted. | | Event | Description | | --------------- | ----------------------------- | | `price.created` | A new price has been created. | | `price.updated` | A price has been updated. | | `price.deleted` | A price has been deleted. | | Event | Description | | ----------------- | ----------------------------------- | | `invoice.created` | A new invoice has been created. | | `invoice.updated` | An invoice has been updated. | | `invoice.paid` | An invoice has been marked as paid. | | `invoice.overdue` | An invoice has become overdue. | | Event | Description | | ------------------------ | ------------------------------------ | | `subscription.created` | A new subscription has been created. | | `subscription.activated` | A subscription has become active. | | `subscription.canceled` | A subscription has been canceled. | | `subscription.renewed` | A subscription has been renewed. | | Event | Description | | ---------------------------- | ------------------------------------------ | | `subscription_phase.created` | A new subscription phase has been created. | | `subscription_phase.updated` | A subscription phase has been updated. | | `subscription_phase.deleted` | A subscription phase has been deleted. | | Event | Description | | ---------------- | ------------------------------ | | `wallet.created` | A new wallet has been created. | | Event | Description | | ------------------------ | ------------------------------------------------ | | `crypto_address.created` | A new crypto address has been added to a wallet. | | `crypto_address.deleted` | A crypto address has been deleted. | | Event | Description | | ------------------------- | ----------------------------------------- | | `payment_claim.submitted` | A customer has submitted a payment claim. | | `payment_claim.approved` | A payment claim has been approved. | | `payment_claim.rejected` | A payment claim has been rejected. | | Event | Description | | ------------------------------ | ----------------------------------------------- | | `crypto_transaction.received` | A crypto transaction has been received. | | `crypto_transaction.allocated` | A transaction has been allocated to an invoice. | | Event | Description | | -------------------------------- | ---------------------------------------------- | | `transaction_allocation.created` | A new transaction allocation has been created. | ## Webhook payload format When an event occurs, TRXN sends a POST request to your endpoint with the following format: ```json theme={null} { "event": "invoice.created", "payload": { "id": "inv_xxx", "object": "invoice", "customer_id": "cus_xxx", "status": "pending", "total_amount": "100.00" }, "timestamp": "2025-01-14T12:00:00Z" } ``` ### Subscription phase payload example ```json theme={null} { "event": "subscription_phase.created", "payload": { "id": "sub_phase_xxx", "object": "subscription_phase", "subscription_id": "sub_xxx", "start_date": "2025-02-01T00:00:00Z", "end_date": "2025-03-01T00:00:00Z", "items": [ { "id": 123, "price_id": "price_xxx", "quantity": 1, "overridden_price_amount": null } ], "created_at": "2025-01-26T12:00:00Z", "updated_at": "2025-01-26T12:00:00Z" }, "timestamp": "2025-01-26T12:00:00Z" } ``` ## Response handling Your endpoint should respond with a 2xx status code to indicate successful receipt. If your endpoint returns a non-2xx status code, TRXN will retry the delivery with exponential backoff (up to 11 attempts). ### Error handling If your endpoint cannot be reached, TRXN will: 1. **Connection error**: Disable the endpoint to prevent further delivery attempts. 2. **Timeout error**: Retry with exponential backoff. 3. **TLS error**: Retry with exponential backoff. 4. **Non-2xx response**: Retry with exponential backoff. ## Event retention TRXN retains webhook events for **30 days**. ## Best practices Return a 2xx response before any complex logic to avoid timeouts. Process the event asynchronously after responding. 1. **Respond quickly**: Return a 2xx response before any complex logic that could cause a timeout. Process the event asynchronously after responding. 2. **Handle duplicates**: Webhook endpoints may occasionally receive the same event more than once. Make your event processing idempotent by tracking the `id` from the payload. If you have already processed an event for that resource, skip it and return a 2xx response. 3. **Handle out-of-order delivery**: Events may not arrive in chronological order. Use the `timestamp` field to determine the sequence of events rather than assuming arrival order. 4. **Always verify signatures**: Use the `X-Trxn-Signature` header to verify all incoming webhooks. Reject any webhook that fails signature verification. 5. **Use HTTPS**: Always use HTTPS endpoints for security. 6. **Store secrets securely**: Store your signing secrets in environment variables or a secrets manager, never in source code. 7. **Monitor failures**: Check webhook events in the dashboard to monitor delivery status. 8. **Regenerate compromised secrets**: If you suspect your signing secret has been compromised, regenerate it immediately. # Billing cycles Source: https://docs.gettrxn.com/guides/billing-cycles Control when recurring charges occur with billing cycle anchors. Billing cycle anchors provide precise control over when recurring charges are billed, enabling businesses to align billing with customer preferences, business processes, or contractual requirements. ## What is a billing cycle anchor? A billing cycle anchor is a specific datetime that determines when recurring billing periods start and end. Instead of billing based on the subscription creation date, the anchor ensures consistent, predictable billing on specific days. ## Why billing anchors matter Without billing anchors, subscriptions created on different days bill on different days: * **Administrative overhead** -- processing payments scattered throughout the month * **Customer confusion** -- unpredictable billing dates make budgeting difficult * **Contractual requirements** -- some agreements require billing on specific dates * **Proration complexity** -- mid-cycle upgrades and downgrades become harder without clear period boundaries ## Proration behaviors Subscriptions support three proration behaviors to handle the period between creation and the billing anchor: | Behavior | Description | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `create_prorations` (default) | Creates a prorated charge for the partial period when the subscription starts before the billing anchor | | `always_invoice` | Always creates an immediate invoice regardless of timing | | `none` | No proration. If a billing anchor is set, waits for the anchor date. If no anchor is set, charges immediately at full price | ## Proration calculations The system calculates prorated amounts with appropriate precision for each interval: * **Daily** -- prorated by hours (for exact time-based billing) * **Weekly** -- prorated by days * **Monthly** -- prorated by days, accounting for varying month lengths * **Yearly** -- prorated by days, handling leap years ## Month-end anchor handling Special logic handles billing anchors set to days 29-31: * **Month-end normalization** -- a billing anchor on the 31st bills on the last day of shorter months (e.g., Feb 28/29, Apr 30) * **Leap year support** -- February 29 anchors correctly handle non-leap years * **Consistent behavior** -- ensures predictable billing even with varying month lengths ## Time preservation For subscriptions with daily intervals, the exact time of the billing anchor is preserved, enabling precise hourly billing for services that require it. ## Scenarios ``` Subscription created: January 15, 2024 Billing anchor: January 31, 2024 Proration behavior: create_prorations Timeline: - Jan 15: Prorated invoice for Jan 15-31 ($54.84 for a $100/month plan) Calculated as: ($100 / 31 days) x 17 days = $54.84 - Jan 31: First full invoice for Feb 1-29 ($100) - Feb 29: Invoice for March ($100) - Mar 31: Invoice for April ($100) ``` Fair billing where customers only pay for what they use. Consolidate all customer billing to the 1st of each month. **Without billing anchors:** ``` Customer A joins March 15 -> Bills on the 15th of each month Customer B joins March 22 -> Bills on the 22nd of each month Customer C joins March 28 -> Bills on the 28th of each month Result: Scattered billing throughout the month ``` **With billing anchors** (anchor = April 1, 2024): ``` Customer A (joins March 15): - March 15: Prorated invoice for Mar 15-31 (17 days = $54.84 on $100/month plan) - April 1: Full invoice for April ($100) - May 1, June 1, etc.: Regular monthly billing ($100) Customer B (joins March 22): - March 22: Prorated invoice for Mar 22-31 (10 days = $32.26 on $100/month plan) - April 1: Full invoice for April ($100) - May 1, June 1, etc.: Regular monthly billing ($100) Customer C (joins March 28): - March 28: Prorated invoice for Mar 28-31 (4 days = $12.90 on $100/month plan) - April 1: Full invoice for April ($100) - May 1, June 1, etc.: Regular monthly billing ($100) ``` Enterprise contract requiring billing on the 15th of each month. ``` Subscription created: June 5, 2024 Billing anchor: June 15, 2024 Timeline: - Jun 5: Prorated charge for Jun 5-15 ($36.67 for $100/month plan) Calculated as: ($100 / 30 days) x 11 days = $36.67 - Jun 15: First full monthly charge ($100) - Jul 15, Aug 15, etc.: Regular monthly billing ($100) ``` Online course or digital product with immediate full access. ``` Subscription created: March 20, 2024 Billing anchor: April 1, 2024 Proration behavior: always_invoice Timeline: - Mar 20: Immediate full invoice ($100) -- customer gets instant access - Apr 1: Next regular invoice ($100) - May 1, June 1, etc.: Regular monthly billing ($100) ``` The customer pays full price on both signup and at the anchor date. This is intentional for immediate-access products. Gym membership at full price regardless of join date. **With billing anchor** (waits for anchor): ``` Subscription created: January 5, 2024 Billing anchor: January 31, 2024 Timeline: - Jan 5: No immediate invoice (waits for anchor) - Jan 31: First full monthly charge ($100) - Feb 29, Mar 31, etc.: Regular monthly billing ($100) ``` **Without billing anchor** (immediate charge): ``` Subscription created: January 5, 2024 Billing anchor: not set Timeline: - Jan 5: Immediate full monthly charge ($100) - Feb 5, Mar 5, etc.: Regular monthly billing ($100) ``` With the `none` behavior: * If `billing_cycle_anchor` **is set**, the system waits for the anchor date * If `billing_cycle_anchor` **is not set**, the system charges immediately at full price 14-day free trial, then monthly billing. ``` Subscription created: March 1, 2024 Billing anchor: March 15, 2024 (14 days later) Proration behavior: none Timeline: - Mar 1: No invoice (trial period starts) - Mar 1-14: Free trial period, full access - Mar 15: First full monthly charge ($100) - Apr 15, May 15, etc.: Regular monthly billing ($100) ``` ## Implementation details The billing anchor functionality is implemented through: * **Database field** -- `billing_cycle_anchor` (datetime) on subscriptions * **Calculation method** -- `calculate_next_billing_date_from_anchor` handles date logic including month-end normalization * **Invoice generation** -- `GenerateInvoicesJob` respects anchors and prevents duplicates * **Initial invoices** -- `generate_initial_invoice` creates prorated or full charges based on the proration behavior ## Implementing consolidated billing To achieve company-wide billing on specific days: For SaaS platforms, `create_prorations` is the most common choice. It ensures fair billing while aligning all customers to the same billing date. **Option 1: Manual setting** Staff explicitly set `billing_cycle_anchor` when creating subscriptions. **Option 2: Business logic layer** ```ruby theme={null} def create_subscription_for_customer(customer, plan) # Company policy: all subscriptions bill on the 1st next_first = Date.today.beginning_of_month next_first = next_first.next_month if Date.today.day > 1 Subscription.create!( customer: customer, billing_cycle_anchor: next_first, proration_behavior: 'create_prorations', # ... other subscription setup ) end ``` **Option 3: Account-level default** Add a `default_billing_day` field to the Account model to automatically set anchors for all new subscriptions. ## Best practices 1. **Set anchors thoughtfully** -- consider your business operations and customer expectations 2. **Communicate clearly** -- inform customers about proration and billing dates 3. **Test edge cases** -- verify behavior around month-ends and leap years 4. **Choose the right proration behavior:** * SaaS platforms often use `create_prorations` * Membership sites might prefer `none` for simplicity * Immediate access services benefit from `always_invoice` ## Related pages Multi-phase subscriptions with flexible pricing and automatic transitions. How invoices are automatically generated from subscriptions. # Crypto payments overview Source: https://docs.gettrxn.com/guides/crypto-payments How cryptocurrency payments work in TRXN. TRXN enables businesses to accept cryptocurrency payments while managing pricing in fiat currency (USD). This page explains the end-to-end payment flow from defining products to allocating received funds. ## Payment flow The cryptocurrency payment lifecycle consists of five stages: Account holders create products with fiat-denominated prices. Each price can optionally include a [slippage margin](/guides/slippage-margin) to accommodate crypto price volatility during payment confirmation. ``` Product: "Pro Plan" └── Price: $99.00/month (USD) └── Slippage margin: 2% ``` Invoices are created either manually or automatically from [subscriptions](/guides/subscriptions). Each invoice contains line items that reference products and prices, establishing the fiat amount owed. For recurring subscriptions, TRXN handles [invoice generation](/guides/invoice-generation) automatically based on billing cycles and phase schedules. Payment addresses are generated or assigned for customers. Each customer has a wallet that holds one or more crypto addresses. When a customer needs to pay an invoice, the system provides the appropriate address for the selected cryptocurrency. ``` Customer: Acme Corp └── Wallet ├── BTC Address: bc1q... └── ETH Address: 0x... ``` TRXN queries the blockchain to find transactions sent to the assigned address within the expected time window. For each transaction found, the system looks up the historical crypto-to-fiat exchange rate at the exact moment of the transaction and calculates the fiat value. See [Transaction verification](/guides/transaction-verification) for the full verification flow. Verified transactions are allocated against outstanding invoices. A single transaction can be split across multiple invoices, and multiple transactions can be combined to satisfy a single invoice. The [slippage margin](/guides/slippage-margin) determines how much price fluctuation is tolerated before a payment is considered complete. ## Supported cryptocurrencies TRXN currently supports: * **BTC** (Bitcoin) * **ETH** (Ethereum) Each cryptocurrency uses blockchain-specific query providers and may have different price data sources. ## How fiat and crypto work together TRXN takes a fiat-first approach to pricing: 1. **Prices are always defined in fiat** (USD). There is no need to set crypto-denominated prices that become stale as markets move. 2. **Conversion happens at verification time.** When a blockchain transaction is detected, TRXN looks up the historical exchange rate at the moment of that transaction to determine the fiat value. 3. **Slippage margin absorbs volatility.** A configurable tolerance percentage prevents invoices from being marked as underpaid due to minor price swings between payment initiation and confirmation. ## Key concepts Multi-phase subscriptions with automatic invoice generation and flexible pricing. Configure price fluctuation tolerance so small swings do not leave invoices underpaid. How TRXN queries the blockchain and converts crypto amounts to fiat values. How recurring invoices are automatically created from active subscriptions. # Invoice generation Source: https://docs.gettrxn.com/guides/invoice-generation How invoices are automatically generated from subscriptions. TRXN automatically generates invoices from active subscriptions through the `GenerateInvoicesJob`. This ensures that recurring charges are billed on time without manual intervention. ## How it works The `GenerateInvoicesJob` runs on a regular schedule, typically daily. It evaluates all subscriptions to determine which ones are due for billing. The job iterates through all subscriptions and checks billing eligibility based on the subscription's next billing date and the current time. Only subscriptions with an active phase containing recurring items are processed. If a subscription has no active phase or the active phase contains only one-time items, it is skipped. The job compares each subscription's `next_billing_date` with the current time. If the billing date has arrived or passed, the subscription is eligible for invoice generation. For each eligible subscription, the job generates an invoice with line items corresponding to the recurring phase items in the active phase. ## Invoice structure When an invoice is generated, it contains: * **Invoice level** -- customer, account, and subscription references * **Line items** -- one line item per subscription phase item * **Pricing** -- uses overridden prices when set, otherwise falls back to the base product price * **Quantities** -- reflects the quantity specified in each phase item ## Example ``` Subscription: John's Enterprise Plan Active Phase: "Regular Pricing" (contains 3 recurring items) Next Billing Date: 2024-01-15 Current Date: 2024-01-15 Generated Invoice: ├── Line Item 1: Base Platform Access -- $199.00 (qty: 1) ├── Line Item 2: Additional User Seats -- $125.00 (qty: 5 x $25) └── Line Item 3: Premium Support -- $50.00 (qty: 1) Total: $374.00 ``` ## Duplicate prevention The invoice generation system includes safeguards to prevent duplicate invoices. If an invoice has already been generated for a billing period, the job will not create another one for the same period. ## Billing cycle anchors Invoice generation respects [billing cycle anchors](/guides/billing-cycles). When a subscription has an anchor set, the `next_billing_date` calculation aligns with the anchor date rather than the subscription creation date. ## Initial invoices When a subscription is first created, the system may generate an initial invoice depending on the [proration behavior](/guides/billing-cycles): | Proration behavior | Initial invoice | | ------------------- | --------------------------------------------------------------------------------- | | `create_prorations` | Prorated charge for the partial period between creation and the billing anchor | | `always_invoice` | Immediate full-price invoice | | `none` | No initial invoice if an anchor is set; immediate full-price invoice if no anchor | ## Related pages Multi-phase subscriptions with flexible pricing and automatic transitions. Control when recurring charges occur with billing cycle anchors. # Sandbox mode Source: https://docs.gettrxn.com/guides/sandboxes Test your integration in an isolated environment. TRXN provides a sandbox mode that lets you test your integration in an isolated environment without affecting production data. Sandbox data is completely separate from live data, so you can create test customers, invoices, and subscriptions without any risk. ## How sandboxes work The sandbox feature isolates data through a `Sandboxable` concern applied to core models. When sandbox mode is active, all data operations are scoped to the current sandbox context: * **Sandbox records** are only visible when the matching sandbox is active * **Production records** (where `sandbox_id` is null) are only visible outside of sandbox mode * Each sandbox has its own isolated dataset -- customers, invoices, subscriptions, and all other tenant-scoped resources Sandbox isolation is enforced at the query level through scoped queries, not through a database-level default scope. This means you must use the `.sandboxed` scope when querying models to ensure proper isolation. ## Using sandbox mode ### Through the application Account users can select a sandbox from their account settings. Once a sandbox is selected, all operations within the application are scoped to that sandbox. Switching back to production mode clears the sandbox context. ### Through the API API tokens can be associated with a specific sandbox. When an API request is made with a sandbox token, the sandbox context is automatically set based on the token's configuration. This means: * **Production API tokens** access production data only * **Sandbox API tokens** access sandbox data only ## Data isolation All core domain models support sandbox isolation: | Model | Sandbox support | | ------------------------ | :-------------: | | Customers | Yes | | Products | Yes | | Prices | Yes | | Invoices | Yes | | Line items | Yes | | Subscriptions | Yes | | Subscription phases | Yes | | Subscription phase items | Yes | | Wallets | Yes | | Crypto addresses | Yes | | Crypto transactions | Yes | | Payment claims | Yes | | Transaction allocations | Yes | | Webhook endpoints | Yes | Sandbox data and production data are completely separate. A customer created in sandbox mode will not be visible in production mode, and vice versa. ## Creating sandbox API tokens To test your integration against the API in sandbox mode: Navigate to your account settings and create a new sandbox environment. Create a new API token and associate it with the sandbox you just created. Include the sandbox API token in your API requests. All data returned and created will be scoped to the sandbox. ```bash theme={null} curl -H "Authorization: Bearer your_sandbox_token" \ https://api.gettrxn.com/api/v1/customers ``` ## Adding sandbox support to new models If you are extending TRXN with custom models, you can add sandbox support by including the `Sandboxable` concern. ### Step 1: Add the migration ```ruby theme={null} class AddSandboxIdToYourModels < ActiveRecord::Migration[7.0] def change add_column :your_models, :sandbox_id, :uuid add_index :your_models, :sandbox_id end end ``` ### Step 2: Include the concern ```ruby theme={null} class YourModel < ApplicationRecord include Sandboxable # Use the sandboxed scope for queries def self.some_query sandboxed.where(...) end end ``` ## Webhooks in sandbox mode Webhook events fired from sandbox data include sandbox context. This means you can set up separate webhook endpoints for sandbox and production, or use a single endpoint that distinguishes between the two based on the payload. ## Related pages End-to-end payment flow from product definition to transaction allocation. Receive real-time notifications for events in your TRXN account. # Slippage margin Source: https://docs.gettrxn.com/guides/slippage-margin Configure price fluctuation tolerance for cryptocurrency payments. Cryptocurrency prices are volatile. Between the moment a customer initiates a payment and when that transaction is confirmed on the blockchain, the fiat value of the crypto can fluctuate. Without any tolerance, a customer who sends the exact amount of crypto to cover a \$100 invoice might end up a few cents short by the time the transaction is verified. The slippage margin solves this by defining a **percentage tolerance** that determines the minimum acceptable payment. If the slippage margin is set to 2%, a $100 invoice accepts any payment of $98.00 or more as paid in full. ## Why slippage margin exists Without slippage margin, businesses face a frustrating cycle: 1. Customer owes \$100 and sends the equivalent in crypto 2. By the time the transaction confirms (minutes to hours later), price fluctuation means the fiat value is now \$99.50 3. The system marks the invoice as partially paid, even though the customer acted in good faith 4. The customer is asked to send another transaction to cover the \$0.50 difference 5. That second transaction also incurs network fees, which may exceed the shortfall Slippage margin eliminates this friction by acknowledging that small fluctuations are a normal part of cryptocurrency payments. ## Formula ``` Minimum Acceptable Amount = Invoice Amount x (1 - Slippage Margin / 100) ``` **Examples:** | Invoice amount | Slippage margin | Minimum acceptable | Result | | :------------: | :-------------: | :----------------: | :-------------------------------------------------- | | \$100.00 | 1% | \$99.00 | Payment of \$99.50 is accepted as paid in full | | \$100.00 | 2% | \$98.00 | Payment of \$98.75 is accepted as paid in full | | \$100.00 | 5% | \$95.00 | Payment of \$96.00 is accepted as paid in full | | \$500.00 | 1% | \$495.00 | Payment of \$497.00 is accepted as paid in full | | \$500.00 | 0.5% | \$497.50 | Payment of \$496.00 is NOT accepted as paid in full | If the total fiat value of all received crypto transactions meets or exceeds the minimum acceptable amount, the payment is considered **paid in full**. If it falls below the minimum, it remains partially paid regardless of the slippage margin. ## The three-level fallback chain Slippage margin can be configured at three levels. The system uses a fallback chain to determine which value applies: ``` Price level ---> Account level ---> System default (most specific) (least specific) ``` ### Level 1: Price-level margin Each individual price can have its own slippage margin. This is the most specific setting and takes priority over all others when set to a value greater than zero. **When to use:** When specific products have different risk profiles. A high-value product might warrant a tighter margin, while a low-value product could tolerate more flexibility. ### Level 2: Account-level default Every account has a default slippage margin that applies to all prices that do not have their own margin set. This provides a single place to control slippage tolerance across all products. **When to use:** When you want a consistent margin across your entire catalog without configuring each price individually. ### Level 3: System default If neither the price nor the account has a slippage margin configured (both are zero), the system applies a built-in default. This ensures that every verification has at least some tolerance. **When to use:** This is automatic. It acts as a safety net so that new accounts and prices have reasonable behavior out of the box. ### Fallback rules The system evaluates the chain from most specific to least specific: 1. If the **price** has a slippage margin greater than zero, use that value 2. Otherwise, if the **account** has a default slippage margin greater than zero, use that value 3. Otherwise, use the **system default** A value of zero at any level means "not configured" and triggers the fallback to the next level. Setting a price's slippage margin to zero does **not** mean "no tolerance" -- it means "use whatever the account or system default is." ### Fallback examples | Scenario | Price margin | Account default | Effective margin | Reason | | ----------------------------- | :----------: | :-------------: | :----------------: | :---------------------------------------- | | Price-level override | 3% | 1% | **3%** | Price-level takes priority | | Account default applies | 0% | 2% | **2%** | Falls back to account | | System default applies | 0% | 0% | **System default** | Falls back to system | | Price-level with tight margin | 0.25% | 5% | **0.25%** | Price-level takes priority, even if lower | ## When slippage is evaluated Slippage margin is applied **at verification time** -- the moment the system checks whether a crypto payment satisfies an invoice. This means: * The slippage value is read from the current configuration when verification runs * It is **not** stored on the invoice or locked in at invoice creation time * It is **not** retroactively applied to previously completed verifications ## Impact of changing slippage margin ### Already-paid invoices are not affected Once an invoice is marked as paid, that status is permanent. Changing the slippage margin has no retroactive effect on invoices that have already been verified and marked as paid. **Example:** * Account has a 1% slippage margin * A $100 invoice receives a $99.20 crypto payment * Verification runs and marks the invoice as paid (since $99.20 >= $99.00) * Account holder later changes slippage to 0.5% * The \$100 invoice remains paid -- it is not re-evaluated ### Unpaid invoices use the new value Invoices in a pending or partially paid state are affected by slippage changes the next time verification runs. **Example:** * Account has a 1% slippage margin * A $100 invoice receives a $99.20 crypto payment but has not yet been verified * Account holder changes slippage to 0.5% (minimum acceptable becomes \$99.50) * Verification now runs: $99.20 < $99.50, so the invoice is not marked as paid Tightening the slippage margin on an account with pending invoices may cause payments that would have been accepted under the old margin to be rejected. Review pending invoices before making changes. ### Tightening slippage on active invoices ``` Before change: Account slippage: 1% Invoice: $200.00 due Minimum acceptable: $198.00 Crypto received: $198.50 Status: Would be PAID if verified now After change (slippage reduced to 0.5%): Account slippage: 0.5% Invoice: $200.00 due Minimum acceptable: $199.00 Crypto received: $198.50 Status: NOT paid in full (shortfall of $0.50) ``` ### Loosening slippage on active invoices ``` Before change: Account slippage: 0.5% Invoice: $200.00 due Minimum acceptable: $199.00 Crypto received: $198.50 Status: NOT paid in full After change (slippage increased to 1%): Account slippage: 1% Invoice: $200.00 due Minimum acceptable: $198.00 Crypto received: $198.50 Status: Would be PAID if verified now ``` ### Summary of change impact | Invoice status | Effect of slippage change | | :----------------- | :------------------------------------------------------------------------------------------------ | | **Paid** | No effect. Status is permanent. | | **Partially paid** | New slippage applies on next verification. May now pass or fail depending on direction of change. | | **Pending** | New slippage applies on next verification. | | **Overdue** | New slippage applies on next verification. | ## Valid slippage values * **Minimum:** 0% (triggers fallback to next level in the chain) * **Maximum:** 100% (would accept any payment amount, including zero -- not recommended) * **Precision:** Up to two decimal places (e.g., 1.50%, 0.25%) ### Practical recommendations | Slippage range | Use case | | :------------: | :---------------------------------------------------------------- | | 0.25% - 0.5% | Tight tolerance for stablecoins or low-volatility assets | | 1% - 2% | Standard tolerance for most cryptocurrency payments | | 3% - 5% | Higher tolerance for volatile assets or longer confirmation times | | > 5% | Unusual -- consider whether the business can absorb this variance | ## Interaction with multiple transactions When a customer pays an invoice across multiple crypto transactions, the slippage margin applies to the **total fiat value** of all transactions combined, not to each individual transaction. **Example** (2% slippage on a $100 invoice, minimum acceptable = $98.00): | Transaction | Crypto amount | Fiat value at time | Running total | Status | | :---------: | :-----------: | :----------------: | :-----------: | :------------- | | Tx 1 | 0.001 BTC | \$48.00 | \$48.00 | Partially paid | | Tx 2 | 0.001 BTC | \$51.00 | \$99.00 | Paid in full | Even though Transaction 1 alone would not satisfy the invoice, the combined total of $99.00 exceeds the $98.00 minimum, so the payment is accepted as paid in full. ## Slippage margin in webhook payloads When price events are broadcast via [webhooks](/guides/webhooks) (`price.created`, `price.updated`, `price.deleted`), the payload includes the `slippage_margin_percent` value. This is the raw price-level value, not the effective value after fallback. External systems can use this to understand the configured slippage for each price. ## Slippage margin in verification results When a payment is verified with a slippage margin greater than zero, the verification result includes: * **`slippage_margin_percent`** -- the slippage percentage that was applied * **`fiat_minimum_acceptable`** -- the calculated minimum amount that would be accepted as paid in full These fields are only present when slippage is actively applied (greater than zero). When no slippage is configured, these fields are omitted and the verification behaves as a strict exact-amount check. ## Related pages How TRXN verifies cryptocurrency payments on the blockchain. End-to-end payment flow from product definition to transaction allocation. # Subscriptions Source: https://docs.gettrxn.com/guides/subscriptions Multi-phase subscriptions with automatic invoice generation. The subscription system enables flexible, time-based billing for customers with support for multi-phase pricing, recurring and one-time charges, prorated billing, and automatic invoice generation. ## Subscription model Subscriptions are built on a hierarchical model: ``` Subscription ├── Subscription Phase (time-based period) │ ├── Subscription Phase Item (product/pricing) │ │ ├── Price reference │ │ ├── Quantity setting │ │ └── Price override (optional) │ └── Subscription Phase Item │ └── ... └── Subscription Phase └── ... ``` ### Subscriptions The top-level container represents a customer's ongoing relationship with your service. Each subscription: * Belongs to a specific customer and account * Must have at least one subscription phase * Derives its start and end dates from its phases * Tracks billing cycles through phase transitions ### Subscription phases Time-bounded periods within a subscription that define different billing terms: * **Start date** -- when this phase begins * **End date** -- when this phase ends (optional for the final phase) * **Multiple items** -- each phase can contain multiple subscription items * **Validation** -- phases cannot overlap and must be sequential ### Subscription phase items Individual billable items within each phase: * **Price reference** -- links to a specific product price * **Quantity** -- how many units of this item (default: 1) * **Price override** -- optional custom pricing that overrides the base price * **Billing type** -- recurring or one-time charges * **Validation** -- each phase must have at least one item, and all recurring items must share the same interval ## Key features ### Datetime precision The subscription system uses full datetime fields (not just dates) for phase boundaries, enabling: * **Hourly billing** -- cloud computing, parking, co-working spaces * **Daily subscriptions** -- equipment rentals, temporary access * **Precise transitions** -- exact moment when trials end and billing begins * **No ambiguity** -- clear timezone-aware datetime boundaries ### Automatic validation Built-in validations prevent billing errors: * **No overlaps** -- phases cannot overlap in time * **No gaps** -- phases must connect seamlessly (`end_date` = next `start_date`) * **Sequential integrity** -- phases automatically validated for proper ordering * **Interval consistency** -- all recurring items in a phase must have matching intervals * **Price constraints** -- overrides must be non-negative (\$0 allowed for free trials) ### Billing cycle anchors Billing cycle anchors control when recurring charges occur. See [Billing cycles](/guides/billing-cycles) for details. ## Creating a subscription ### Minimum requirements To create a subscription, you need: 1. **Account** -- the business account that owns the subscription 2. **Customer** -- the customer receiving the subscription 3. **At least one phase** with a start date 4. **At least one phase item** linking to a price ### Basic subscription ```ruby theme={null} # Simple monthly subscription subscription = Subscription.new( account: account, customer: customer, billing_cycle_anchor: Time.zone.parse("2024-02-01"), # Optional proration_behavior: 'create_prorations' # Optional, defaults to 'create_prorations' ) # Add a single phase phase = subscription.subscription_phases.build( start_date: Time.current, end_date: nil # nil means ongoing/no end date ) # Add items to the phase phase.subscription_phase_items.build( price: monthly_price, quantity: 1 ) subscription.save! ``` ### Multi-phase subscription ```ruby theme={null} # Create a subscription with trial, promotional, and regular pricing phases subscription = Subscription.new( account: account, customer: customer ) # Phase 1: 14-day free trial trial_start = Time.current trial_end_date = trial_start + 14.days trial_phase = subscription.subscription_phases.build( start_date: trial_start, end_date: trial_end_date ) trial_phase.subscription_phase_items.build( price: monthly_plan_price, quantity: 1, overridden_price_amount: 0.00 # Free trial ) # Phase 2: 3-month promotional pricing (starts when trial ends) promo_end_date = trial_end_date + 3.months promo_phase = subscription.subscription_phases.build( start_date: trial_end_date, end_date: promo_end_date ) promo_phase.subscription_phase_items.build( price: monthly_plan_price, quantity: 1, overridden_price_amount: 19.99 # Discounted from regular $49.99 ) # Phase 3: Regular pricing (ongoing, starts when promo ends) regular_phase = subscription.subscription_phases.build( start_date: promo_end_date, end_date: nil # Ongoing ) regular_phase.subscription_phase_items.build( price: monthly_plan_price, quantity: 1 # No override - uses the price's standard amount ) subscription.save! ``` ## Validation rules ### Subscription level * Must have at least one subscription phase * Cannot exist without phases at any time ### Phase level * Must have at least one subscription phase item * Start date required on all phases * End date required if subsequent phases exist * Phases cannot overlap * Phases must be sequential without gaps -- the `end_date` of one phase must exactly match the `start_date` of the next * Supports hourly, daily, weekly, monthly, and yearly precision ### Phase item level * Must reference a valid price * Quantity must be a positive integer (minimum 1) * Price overrides must be non-negative (can be \$0.00 for free trials) * All recurring items in a phase must have the same interval type and count ## Sequential phases When creating multi-phase subscriptions, phases must connect seamlessly: ```ruby theme={null} # Correct: phases connect exactly phase_1 = subscription.subscription_phases.build( start_date: Time.zone.parse("2024-01-01 00:00:00"), end_date: Time.zone.parse("2024-01-31 23:59:59") ) phase_2 = subscription.subscription_phases.build( start_date: Time.zone.parse("2024-01-31 23:59:59"), end_date: Time.zone.parse("2024-02-29 23:59:59") ) ``` Gaps between phases will fail validation. The `end_date` of one phase must exactly match the `start_date` of the next phase, down to the second. ### Hourly subscription example ```ruby theme={null} # Co-working space with hourly billing subscription = Subscription.new(account: account, customer: customer) # Morning session: 9 AM - 1 PM morning_phase = subscription.subscription_phases.build( start_date: Time.zone.parse("2024-01-15 09:00:00"), end_date: Time.zone.parse("2024-01-15 13:00:00") ) morning_phase.subscription_phase_items.build( price: hourly_desk_price, # $15/hour quantity: 4 ) # Afternoon session: 1 PM - 5 PM (connects seamlessly) afternoon_phase = subscription.subscription_phases.build( start_date: Time.zone.parse("2024-01-15 13:00:00"), end_date: Time.zone.parse("2024-01-15 17:00:00") ) afternoon_phase.subscription_phase_items.build( price: hourly_desk_price, quantity: 4 ) subscription.save! ``` ## Interval consistency All recurring items within a single subscription phase must have consistent billing intervals. This prevents ambiguity in invoice generation and billing calculations. ### Valid configurations ```ruby theme={null} # All recurring items have the same interval (monthly, count 1) phase.subscription_phase_items.build(price: monthly_plan_price, quantity: 1) # month/1 phase.subscription_phase_items.build(price: monthly_addon_price, quantity: 2) # month/1 phase.subscription_phase_items.build(price: one_time_setup_fee, quantity: 1) # non-recurring (allowed) # All recurring items have quarterly billing (monthly, count 3) phase.subscription_phase_items.build(price: quarterly_base_price, quantity: 1) # month/3 phase.subscription_phase_items.build(price: quarterly_support_price, quantity: 1) # month/3 # Different phases can have different intervals phase_1.subscription_phase_items.build(price: monthly_price, quantity: 1) # Phase 1: monthly phase_2.subscription_phase_items.build(price: yearly_price, quantity: 1) # Phase 2: yearly ``` ### Invalid configurations ```ruby theme={null} # Mixed interval types (monthly vs daily) -- will fail validation phase.subscription_phase_items.build(price: monthly_price, quantity: 1) # month/1 phase.subscription_phase_items.build(price: daily_price, quantity: 1) # day/1 # Same interval type but different counts -- will fail validation phase.subscription_phase_items.build(price: monthly_price, quantity: 1) # month/1 phase.subscription_phase_items.build(price: quarterly_price, quantity: 1) # month/3 ``` Non-recurring items are exempt from interval consistency rules. One-time charges can coexist with any recurring interval type within the same phase. ## Database structure ```sql theme={null} subscriptions ├── customer_id (required) ├── account_id (required) ├── billing_cycle_anchor (datetime, optional) ├── proration_behavior (string, default: "create_prorations") └── sandbox_id (optional) subscription_phases ├── subscription_id (required) ├── start_date (datetime, required) ├── end_date (datetime, optional) └── sandbox_id (optional) subscription_phase_items ├── subscription_phase_id (required) ├── price_id (required) ├── quantity (integer, default: 1, must be >= 1) ├── overridden_price_amount (decimal, optional, must be >= 0) └── sandbox_id (optional) ``` `start_date` and `end_date` are datetime fields (not date fields), enabling precise time-based billing for hourly and daily subscriptions. ## Key methods | Method | Description | | -------------------------------- | ----------------------------------------------------------------- | | `subscription.active_phase` | Returns the current phase based on `Time.current` | | `subscription.total_amount` | Calculates the total recurring cost of the active phase | | `subscription.next_billing_date` | Determines when the next invoice should be generated | | `phase_item.effective_price` | Returns `overridden_price_amount` or falls back to `price.amount` | | `subscription.start_date` | Returns the `start_date` of the first phase | | `subscription.end_date` | Returns the `end_date` of the active phase | ## Pricing flexibility ### Price overrides Phase items can override the base price of a product: * **Use case**: promotional pricing, volume discounts, custom contracts, free trials * **Validation**: must be non-negative (>= 0), can be \$0.00 for free trials * **Fallback**: uses the product's base price if no override is set (nil) ### Quantity support Each phase item supports configurable quantities: * **Use case**: per-seat licensing, usage-based billing, bulk purchases * **Calculation**: `quantity x (overridden_price_amount || price.amount)` ### Billing intervals Phase items inherit billing behavior from their associated prices: * **Recurring**: monthly, weekly, yearly billing cycles * **One-time**: charges that appear only once per phase ## Phase transitions Subscriptions automatically transition between phases based on date ranges: ``` Phase 1: Trial Period (Day 1-30) ├── Basic Plan -- $0/month (overridden from $29/month) └── Setup Fee -- $10 (one-time) Phase 2: Discounted Period (Day 31-365) ├── Basic Plan -- $19/month (overridden from $29/month) └── Premium Feature Add-on -- $5/month Phase 3: Regular Pricing (Day 366+) ├── Basic Plan -- $29/month (standard pricing) └── Premium Feature Add-on -- $5/month ``` At any given time, the subscription has one active phase -- the phase whose date range includes the current time. The active phase determines current billing amounts and drives invoice generation. ## Real-world scenarios ``` Phase 1 (Days 1-30): Free trial ├── Core Platform Access -- $0/month (overridden from $99) ├── Users Included -- 2 seats └── Support Level -- Basic (email only) Phase 2 (Days 31-120): Post-trial with onboarding ├── Core Platform Access -- $99/month ├── Users Included -- 5 seats ├── Onboarding Package -- $500 (one-time) ├── Dedicated Success Manager -- $0/month (included for first 3 months) └── Support Level -- Premium Phase 3 (Days 121-365): Growth phase ├── Core Platform Access -- $99/month ├── Additional User Seats -- $20/month x 5 ├── API Access -- $49/month └── Support Level -- Premium Phase 4 (Day 366+): Enterprise ├── Core Platform Access -- $79/month (volume discount) ├── Unlimited Users -- $500/month (flat rate) ├── API Access -- $49/month ├── Custom Integrations -- $200/month └── Support Level -- Enterprise (24/7) ``` ``` Phase 1: Fall Semester (Aug 15 - Dec 15) ├── Platform Access -- $49/month ├── Course Materials -- $100 (one-time) ├── Tutoring Hours -- 5 hours/month included └── Exam Proctoring -- $25 per exam (one-time) Phase 2: Winter Break (Dec 16 - Jan 14) ├── Platform Access -- $9.99/month (reduced rate) └── Study Materials Access -- Included Phase 3: Spring Semester (Jan 15 - May 15) ├── Platform Access -- $49/month ├── Course Materials -- $100 (one-time) ├── Tutoring Hours -- 5 hours/month included └── Exam Proctoring -- $25 per exam Phase 4: Summer Session (May 16 - Aug 14) ├── Platform Access -- $29/month (summer rate) ├── Course Materials -- $50 (reduced) └── Tutoring Hours -- 2 hours/month included ``` ``` Phase 1 (Months 1-3): Explorer ├── Gym Access -- $79/month ├── Classes Included -- 4/month └── Personal Training Session -- $80 (one-time welcome session) Phase 2 (Months 4-6): Dedicated ├── Gym Access -- $69/month (reward for staying) ├── Classes Included -- 6/month └── Quarterly Fitness Assessment -- $0 (included) Phase 3 (Months 7-12): Champion ├── Gym Access -- $59/month ├── Classes Included -- 8/month ├── Guest Passes -- 2/month └── Quarterly Fitness Assessment -- $0 (included) Phase 4 (Month 13+): Elite ├── Gym Access -- $49/month (best rate) ├── Unlimited Classes -- Included ├── Guest Passes -- 4/month ├── Personal Training Session -- 1/quarter included └── Premium Locker -- Included ``` ``` Phase 1: Discovery and planning (Month 1) ├── Discovery Workshop -- $5,000 (one-time) ├── Stakeholder Interviews -- $2,000 (one-time) ├── Strategic Planning -- $150/hour x 40 hours └── Project Management -- $1,500/month Phase 2: Implementation (Months 2-4) ├── Development Work -- $175/hour x 160 hours/month ├── Weekly Progress Meetings -- Included ├── Project Management -- $2,500/month └── Cloud Infrastructure -- $500/month Phase 3: Launch and stabilization (Month 5) ├── Launch Support -- $10,000 (one-time) ├── On-call Support -- $200/hour (as needed) ├── Performance Optimization -- $5,000 (one-time) └── Training Sessions -- $1,000 per session x 4 Phase 4: Maintenance and support (Month 6+) ├── Monthly Retainer -- $5,000/month (includes 20 hours) ├── Additional Hours -- $150/hour (beyond retainer) ├── Quarterly Reviews -- Included └── Priority Support SLA -- Included ``` ``` Phase 1 (Months 1-3): New subscriber special ├── Monthly Box -- $9.99/month (overridden from $39.99) ├── Shipping -- FREE (overridden from $5.99) ├── Welcome Gift -- $20 value (one-time) └── Exclusive Member Forum -- Included Phase 2 (Months 4-12): Regular pricing with loyalty perks ├── Monthly Box -- $39.99/month ├── Shipping -- $5.99/month ├── Quarterly Bonus Item -- $15 value (every 3 months) └── Member Forum -- Included Phase 3 (Month 13+): VIP status ├── Monthly Box -- $39.99/month ├── Shipping -- FREE (reward for loyalty) ├── Quarterly Bonus Item -- $25 value ├── Early Access -- New products 48hrs early └── VIP Concierge Service -- Included ``` ``` Phase 1: Startup plan (0-10 employees) ├── Platform License -- $299/month ├── Users Included -- Up to 10 ├── Data Storage -- 100GB included ├── API Calls -- 10,000/month included └── Support -- Business hours email Phase 2: Growth plan (11-50 employees) ├── Platform License -- $999/month ├── Users Included -- Up to 50 ├── Data Storage -- 500GB included ├── API Calls -- 100,000/month included ├── Custom Integrations -- 2 included └── Support -- Priority phone and email Phase 3: Scale plan (51-200 employees) ├── Platform License -- $2,999/month ├── Users Included -- Up to 200 ├── Data Storage -- 2TB included ├── API Calls -- 1,000,000/month included ├── Custom Integrations -- 5 included ├── Advanced Analytics -- Included └── Support -- 24/7 with 1-hour SLA Phase 4: Enterprise (201+ employees) ├── Platform License -- Custom negotiated base ├── Users -- Unlimited ├── Data Storage -- Unlimited ├── API Calls -- Unlimited ├── Custom Integrations -- Unlimited ├── Professional Services -- 40 hours/month included └── Support -- Dedicated team with 15-minute SLA ``` ## Best practices Always use `Time.zone.parse()` or `Time.current` for phase boundaries, not `Date.parse()` or `Date.today`. Datetime precision prevents ambiguity in phase transitions. 1. **Sequential phases** -- ensure the `end_date` of one phase exactly matches the `start_date` of the next 2. **Clear transition points** -- define specific datetimes for phase transitions, considering time zones 3. **Consistent intervals** -- all recurring items within a phase must have the same interval type and count 4. **Value progression** -- each phase should provide clear value changes to justify pricing changes 5. **Test your phases** -- validate that phases connect properly and intervals are consistent before deploying to production 6. **Grace periods** -- consider buffer time between phases for major pricing increases (but ensure no gaps in datetime terms) 7. **Communication** -- notify customers before phase transitions that affect pricing 8. **Price override validation** -- price overrides can be \$0.00 (free trials) but not negative 9. **Last phase open-ended** -- the final phase can have `end_date: nil` for ongoing subscriptions ## Related pages Control when recurring charges occur with billing cycle anchors. How invoices are automatically generated from subscriptions. # Transaction verification Source: https://docs.gettrxn.com/guides/transaction-verification How TRXN verifies cryptocurrency payments on the blockchain. When a customer pays an invoice with cryptocurrency, TRXN verifies that the payment was actually made and converts the crypto amount to fiat value at the time of the transaction. This verification answers the question: **did the customer pay the correct amount?** ## Verification flow TRXN queries the blockchain to find all transactions from the sender address to the recipient address within the expected date range. This returns transaction hashes, timestamps, and crypto amounts. The blockchain provider is selected through the `TransactionProviderFactory`. The current provider is Bitquery. For each transaction found, TRXN looks up the crypto-to-fiat exchange rate at the exact moment the transaction occurred. For example: "What was 0.002 BTC worth in USD on Jan 15 at 10:00 AM?" The price provider is selected through the `CurrencyQuoteProviderFactory`. Current providers include CoinMarketCap and CoinAPI. Each transaction's crypto amount is multiplied by the historical price to get the fiat value at the time of payment. ``` 0.002 BTC x $50,000/BTC = $100 USD ``` The system sums all transaction fiat values and compares the total to the invoice amount. This step determines: * Total amount paid (in fiat) * Whether the payment is complete or partial * Remaining balance (if any) The [slippage margin](/guides/slippage-margin) is applied at this step to accommodate price fluctuations. ## Verification result After verification completes, the result contains: ```json theme={null} { "paid_in_full": true, "amount_paid": 100.00, "amount_due": 100.00, "remaining": 0.00, "transactions": [...] } ``` When slippage margin is applied, the result also includes: ```json theme={null} { "slippage_margin_percent": 2.0, "fiat_minimum_acceptable": 98.00 } ``` ## Why historical prices matter Cryptocurrency prices are volatile. A payment of 0.002 BTC could be worth: * \$80 on Monday * \$100 on Tuesday * \$95 on Wednesday TRXN uses the price **at the exact moment of the transaction** to determine the fiat value. This ensures fair verification regardless of when the verification is performed. ## Multiple transactions Customers may pay an invoice across multiple transactions. TRXN sums all transactions to determine the total paid: | Transaction | Amount | Fiat value | Running total | | :-----------: | :-------: | :--------: | :-----------: | | Tx 1 (Jan 10) | 0.001 BTC | \$50.00 | \$50.00 | | Tx 2 (Jan 15) | 0.001 BTC | \$52.00 | \$102.00 | The total paid is $102.00. If the invoice amount is $100.00, the payment is complete. Each transaction's fiat value is calculated independently using the exchange rate at its own timestamp. This means two transactions of the same crypto amount may have different fiat values. ## Supported cryptocurrencies Currently supported: * **BTC** (Bitcoin) * **ETH** (Ethereum) Each cryptocurrency uses blockchain-specific query providers and may have different price data sources. ## Verification diagram ``` ┌──────────────────────────────────────────────┐ │ Verification request │ │ │ │ "Verify that address A sent $100 USD worth │ │ of BTC to address B between Jan 1-31" │ └──────────────────┬───────────────────────────┘ │ v ┌──────────────────────────────────────────────┐ │ Step 1: Fetch blockchain data │ │ │ │ Query blockchain for transactions from A │ │ to B within the date range. │ │ │ │ Provider: TransactionProviderFactory │ └──────────────────┬───────────────────────────┘ │ v ┌──────────────────────────────────────────────┐ │ Step 2: Get historical prices │ │ │ │ For each transaction, look up the exchange │ │ rate at the exact moment it occurred. │ │ │ │ Provider: CurrencyQuoteProviderFactory │ └──────────────────┬───────────────────────────┘ │ v ┌──────────────────────────────────────────────┐ │ Step 3: Calculate fiat values │ │ │ │ crypto amount x historical price = fiat │ │ 0.002 BTC x $50,000/BTC = $100 USD │ └──────────────────┬───────────────────────────┘ │ v ┌──────────────────────────────────────────────┐ │ Step 4: Verify payment status │ │ │ │ Sum all fiat values. Compare to invoice. │ │ Apply slippage margin if configured. │ └──────────────────┬───────────────────────────┘ │ v ┌──────────────────────────────────────────────┐ │ Verification result │ │ │ │ paid_in_full: true │ │ amount_paid: 100.00 │ │ amount_due: 100.00 │ │ remaining: 0.00 │ └──────────────────────────────────────────────┘ ``` ## Related pages Configure price fluctuation tolerance for cryptocurrency payments. End-to-end payment flow from product definition to transaction allocation. # Webhooks Source: https://docs.gettrxn.com/guides/webhooks Receive real-time notifications for events in your TRXN account. Webhooks allow your application to receive real-time HTTP notifications when events occur in your TRXN account. Instead of polling the API for changes, you register a URL and TRXN sends a POST request to that URL whenever a relevant event fires. ## How webhooks work Register a webhook endpoint URL and choose which events to subscribe to. You can subscribe to specific events or use `["*"]` to receive all events. See the [webhook endpoints API reference](/api-reference/webhook-endpoints) for CRUD operations. When you create an endpoint, TRXN returns a signing secret (format: `whsec_<64_hex_chars>`). Store this securely -- it is only shown in full once, at creation time. When a subscribed event occurs, TRXN sends an HTTP POST request to your endpoint with a JSON payload describing the event. Your server verifies the request signature, returns a 2xx response, then processes the event asynchronously. ## Payload format Every webhook delivery sends a JSON body with this structure: ```json theme={null} { "event": "invoice.created", "payload": { "id": "inv_xxx", "object": "invoice", "customer_id": "cus_xxx", "status": "pending", "total_amount": "100.00" }, "timestamp": "2025-01-14T12:00:00Z" } ``` ## Signature verification TRXN signs every webhook request with HMAC-SHA256 so you can verify that the request genuinely came from TRXN. ### Signature header format Every webhook request includes an `X-Trxn-Signature` header: ``` X-Trxn-Signature: t=,v1= ``` **Example:** ``` X-Trxn-Signature: t=1704067200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd ``` ### Verification steps 1. Extract the timestamp (`t`) and signature (`v1`) from the header 2. Check that the timestamp is within 5 minutes of the current time (prevents replay attacks) 3. Compute the expected signature: `HMAC-SHA256(secret, ".")` 4. Compare signatures using constant-time comparison Use the full signing secret including the `whsec_` prefix as the HMAC key. This matches Stripe's implementation pattern. ### Code examples ```ruby Ruby theme={null} def verify_webhook(payload, signature_header, secret) parts = signature_header.split(",").to_h { |p| p.split("=", 2) } timestamp = parts["t"].to_i signature = parts["v1"] # Check timestamp is within 5 minutes return false if (Time.now.to_i - timestamp).abs > 300 # Compute expected signature (use full secret including whsec_ prefix) signed_payload = "#{timestamp}.#{payload}" expected = OpenSSL::HMAC.hexdigest("SHA256", secret, signed_payload) # Constant-time comparison ActiveSupport::SecurityUtils.secure_compare(expected, signature) end ``` ```javascript Node.js theme={null} const crypto = require('crypto'); function verifyWebhook(payload, signatureHeader, secret) { const parts = Object.fromEntries( signatureHeader.split(',').map(p => p.split('=')) ); const timestamp = parseInt(parts.t, 10); const signature = parts.v1; // Check timestamp is within 5 minutes if (Math.abs(Date.now() / 1000 - timestamp) > 300) { return false; } // Compute expected signature (use full secret including whsec_ prefix) const signedPayload = `${timestamp}.${payload}`; const expected = crypto .createHmac('sha256', secret) .update(signedPayload) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(signature) ); } ``` ```python Python theme={null} import hmac import hashlib import time def verify_webhook(payload: str, signature_header: str, secret: str) -> bool: parts = dict(p.split("=", 1) for p in signature_header.split(",")) timestamp = int(parts["t"]) signature = parts["v1"] # Check timestamp is within 5 minutes if abs(time.time() - timestamp) > 300: return False # Compute expected signature (use full secret including whsec_ prefix) signed_payload = f"{timestamp}.{payload}" expected = hmac.new( secret.encode(), signed_payload.encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature) ``` ## Available events ### Customer events | Event | Description | | ------------------ | ------------------------------- | | `customer.created` | A new customer has been created | | `customer.updated` | A customer has been updated | | `customer.deleted` | A customer has been deleted | ### Product events | Event | Description | | ----------------- | ------------------------------ | | `product.created` | A new product has been created | | `product.updated` | A product has been updated | | `product.deleted` | A product has been deleted | ### Price events | Event | Description | | --------------- | ---------------------------- | | `price.created` | A new price has been created | | `price.updated` | A price has been updated | | `price.deleted` | A price has been deleted | ### Invoice events | Event | Description | | ----------------- | ---------------------------------- | | `invoice.created` | A new invoice has been created | | `invoice.updated` | An invoice has been updated | | `invoice.paid` | An invoice has been marked as paid | | `invoice.overdue` | An invoice has become overdue | ### Subscription events | Event | Description | | ------------------------ | ----------------------------------- | | `subscription.created` | A new subscription has been created | | `subscription.activated` | A subscription has become active | | `subscription.canceled` | A subscription has been canceled | | `subscription.renewed` | A subscription has been renewed | ### Subscription phase events | Event | Description | | ---------------------------- | ----------------------------------------- | | `subscription_phase.created` | A new subscription phase has been created | | `subscription_phase.updated` | A subscription phase has been updated | | `subscription_phase.deleted` | A subscription phase has been deleted | **Payload example:** ```json theme={null} { "event": "subscription_phase.created", "payload": { "id": "sub_phase_xxx", "object": "subscription_phase", "subscription_id": "sub_xxx", "start_date": "2025-02-01T00:00:00Z", "end_date": "2025-03-01T00:00:00Z", "items": [ { "id": 123, "price_id": "price_xxx", "quantity": 1, "overridden_price_amount": null } ], "created_at": "2025-01-26T12:00:00Z", "updated_at": "2025-01-26T12:00:00Z" }, "timestamp": "2025-01-26T12:00:00Z" } ``` ### Wallet events | Event | Description | | ---------------- | ----------------------------- | | `wallet.created` | A new wallet has been created | ### Crypto address events | Event | Description | | ------------------------ | ----------------------------------------------- | | `crypto_address.created` | A new crypto address has been added to a wallet | | `crypto_address.deleted` | A crypto address has been deleted | ### Payment claim events | Event | Description | | ------------------------- | ---------------------------------------- | | `payment_claim.submitted` | A customer has submitted a payment claim | | `payment_claim.approved` | A payment claim has been approved | | `payment_claim.rejected` | A payment claim has been rejected | ### Crypto transaction events | Event | Description | | ------------------------------ | ---------------------------------------------- | | `crypto_transaction.received` | A crypto transaction has been received | | `crypto_transaction.allocated` | A transaction has been allocated to an invoice | ### Transaction allocation events | Event | Description | | -------------------------------- | --------------------------------------------- | | `transaction_allocation.created` | A new transaction allocation has been created | ## Response handling Your endpoint must respond with a 2xx status code to indicate successful receipt. If your endpoint returns a non-2xx status code, TRXN retries with exponential backoff for up to 11 attempts. ### Error handling | Error type | Behavior | | ---------------- | --------------------------------------------------------- | | Connection error | Endpoint is disabled to prevent further delivery attempts | | Timeout error | Retry with exponential backoff | | TLS error | Retry with exponential backoff | | Non-2xx response | Retry with exponential backoff | ## Event retention TRXN retains webhook events for **30 days**. ## Best practices Return a 2xx response **before** any complex processing. Process the event asynchronously after responding to avoid timeouts. 1. **Respond quickly** -- return a 2xx response before any complex logic that could cause a timeout. Process the event asynchronously after responding. 2. **Handle duplicates** -- webhook endpoints may occasionally receive the same event more than once. Make your event processing idempotent by tracking the `id` from the payload. 3. **Handle out-of-order delivery** -- events may not arrive in chronological order. Use the `timestamp` field to determine the sequence rather than assuming arrival order. 4. **Always verify signatures** -- use the `X-Trxn-Signature` header to verify all incoming webhooks. Reject any webhook that fails signature verification. 5. **Use HTTPS** -- always use HTTPS endpoints for security. 6. **Store secrets securely** -- store your signing secrets in environment variables or a secrets manager, never in source code. 7. **Monitor failures** -- check webhook events in the dashboard to monitor delivery status. 8. **Regenerate compromised secrets** -- if you suspect your signing secret has been compromised, regenerate it immediately via the API. ## Managing webhook endpoints For creating, updating, deleting, and regenerating secrets for webhook endpoints, see the [webhook endpoints API reference](/api-reference/webhook-endpoints). # Introduction Source: https://docs.gettrxn.com/index Accept and verify cryptocurrency payments against invoices with TRXN. TRXN enables businesses to accept cryptocurrency as payment while managing pricing in fiat currency (USD). Define products with fiat prices, generate invoices, and verify blockchain payments automatically. ## How it works Create products with fiat-denominated prices. Set optional slippage margins to account for crypto price fluctuations. Create invoices with line items for your customers. Invoices track amounts owed in fiat. Customers pay using cryptocurrency (BTC, ETH). Assign crypto addresses to receive payments. TRXN monitors blockchain data, converts amounts to fiat at the transaction time, and allocates payments to outstanding invoices. ## Key features Price products in USD. Accept BTC and ETH. TRXN handles the conversion math. Multi-phase subscriptions with trials, promotional pricing, and automatic invoice generation. Automatic blockchain verification with historical price lookup at the exact transaction time. 30+ event types with HMAC-SHA256 signed payloads for real-time integrations. Isolated testing environment with separate data, tokens, and full API access. Account-based tenancy with team management, roles, and per-account data isolation. ## Supported cryptocurrencies | Currency | Symbol | Address format | | -------- | ------ | --------------------------- | | Bitcoin | BTC | Standard Bitcoin addresses | | Ethereum | ETH | ERC-20 compatible addresses | ## Next steps Get up and running in 5 minutes. Explore the full API. # Quickstart Source: https://docs.gettrxn.com/quickstart Get up and running with TRXN in 5 minutes. This guide walks you through creating your first customer, product, and invoice using the TRXN API. ## Prerequisites * A TRXN account ([sign up](https://gettrxn.com/users/sign_up)) * An API token (create one in **Settings > API Tokens**) ## 1. Authenticate All API requests require a Bearer token in the `Authorization` header. ```bash theme={null} export TRXN_TOKEN="your_api_token_here" ``` Test your token: ```bash theme={null} curl https://api.gettrxn.com/v1/me \ -H "Authorization: Bearer $TRXN_TOKEN" ``` ## 2. Create a customer ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/customers \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "alice@example.com", "first_name": "Alice", "last_name": "Smith" }' ``` ```json Response theme={null} { "id": "cust_abc123def456", "email": "alice@example.com", "first_name": "Alice", "last_name": "Smith", "created_at": "2025-01-15T14:30:00Z", "updated_at": "2025-01-15T14:30:00Z" } ``` Save the `id` — you'll need it for invoices. ## 3. Create a product and price ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/products \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "Pro Plan"}' ``` Then create a price for the product: ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/prices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_abc123", "amount": 4900, "currency": "usd", "price_type": "one_time" }' ``` Amounts are in cents. `4900` = \$49.00 USD. ## 4. Create an invoice ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/invoices \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cust_abc123def456", "line_items": [ { "price_id": "price_xyz789", "quantity": 1 } ] }' ``` ```json Response theme={null} { "id": "inv_def456ghi789", "customer_id": "cust_abc123def456", "status": "pending", "line_items": [ { "price_id": "price_xyz789", "quantity": 1, "amount": 4900 } ], "created_at": "2025-01-15T14:35:00Z" } ``` ## 5. Add a crypto address Add a Bitcoin or Ethereum address to receive payments: ```bash theme={null} curl -X POST https://api.gettrxn.com/v1/crypto_addresses \ -H "Authorization: Bearer $TRXN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "currency": "btc" }' ``` ## 6. Receive and verify payments When a customer sends crypto to your address, TRXN: 1. Detects the transaction on the blockchain 2. Looks up the historical price at the exact transaction time 3. Converts the crypto amount to fiat (USD) 4. Applies the [slippage margin](/guides/slippage-margin) tolerance 5. Allocates the payment to outstanding invoices You can also [set up webhooks](/guides/webhooks) to receive real-time notifications when payments are verified. ## Next steps Set up recurring billing with multi-phase subscriptions. Get notified when payments arrive, invoices change, and more. Test your integration without affecting production data. Explore the full API.