# Cancel Payment Link Source: https://docs.withgale.com/api-reference/endpoint/cancel-payment-link POST /api/v2/payment-links/{id}/cancel Cancel an active payment link # Cancel Payment Link Cancel an active payment link to prevent it from being used. Only links with `active` status can be cancelled. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Path Parameters | Parameter | Type | Required | Description | | :-------- | :----- | :------- | :---------------------------------------- | | `id` | string | Yes | Payment link ID (e.g., `plink_abc123xyz`) | ## Request Body ```json theme={null} { "reason": "Customer requested cancellation" } ``` | Parameter | Type | Required | Description | | :-------- | :----- | :------- | :-------------------------------------- | | `reason` | string | No | Reason for cancellation (max 500 chars) | ## Response > All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ```json theme={null} { "id": "plink_abc123xyz", "amount": 4995, "currency": "USD", "description": "Premium Blood Pressure Monitor", "status": "cancelled", "cancelled_at": "2026-02-25T16:00:00Z", "cancellation_reason": "Customer requested cancellation" } ``` ## Example ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/payment-links/plink_abc123xyz/cancel \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "reason": "Order cancelled by customer" }' ``` ## Cancellation Rules | Current Status | Can Cancel? | Notes | | :------------- | :---------- | :----------------------------------------------------------------- | | `active` | Yes | Status changes to `cancelled` | | `paid` | No | Use [Create Refund](/api-reference/endpoint/create-refund) instead | | `expired` | No | Already expired | | `cancelled` | No | Already cancelled | Cancelled links cannot be reactivated. Create a new link if needed. ## Webhooks Cancelling a link triggers a `payment_link.cancelled` webhook event. See [Webhooks Reference](/developer-manual/webhooks) for details. ## Errors | Status Code | Error Code | Description | | :---------- | :-------------- | :------------------------------- | | 400 | `invalid_state` | Link is not in cancellable state | | 401 | `unauthorized` | Invalid or missing API key | | 404 | `not_found` | Payment link not found | ## Related * [Create Payment Link](/api-reference/endpoint/create-payment-link) * [Get Payment Link](/api-reference/endpoint/get-payment-link) * [Create Refund](/api-reference/endpoint/create-refund) # Cancel Subscription Source: https://docs.withgale.com/api-reference/endpoint/cancel-subscription POST /api/v2/subscriptions/{id}/cancel Cancel a subscription # Cancel Subscription Cancel a subscription either immediately or at the end of the current billing period. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Path Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | --------------- | | `id` | string | Yes | Subscription ID | ## Request Body ```json theme={null} { "cancel_at_period_end": true, "reason": "customer_request" } ``` | Parameter | Type | Required | Description | | ---------------------- | ------- | -------- | ---------------------------------------------- | | `cancel_at_period_end` | boolean | No | If true, cancel at period end (default: false) | | `reason` | string | No | Cancellation reason | ## Response ```json theme={null} { "id": "sub_abc123xyz", "status": "cancelled", "cancel_at_period_end": true, "current_period_end": "2025-11-18T14:30:00Z", "cancelled_at": "2025-10-20T10:00:00Z" } ``` ## Examples ### Cancel at Period End ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/subscriptions/sub_abc123xyz/cancel \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "cancel_at_period_end": true, "reason": "customer_request" }' ``` ### Immediate Cancellation ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/subscriptions/sub_abc123xyz/cancel \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "cancel_at_period_end": false }' ``` ## Related Endpoints * [Get Subscription](/api-reference/endpoint/get-subscription) * [List Subscriptions](/api-reference/endpoint/list-subscriptions) # Check Eligibility Source: https://docs.withgale.com/api-reference/endpoint/check-eligibility POST /api/v2/products/check-eligibility Check if a product is HSA/FSA eligible before creating it # Check Eligibility Check if a product is HSA/FSA eligible using its UPC code or name, without creating a product. Useful for pre-validation during product import or catalog setup. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Request Body ```json theme={null} { "upc_code_or_gtin": "14567890123456", "product_name": "Digital Blood Pressure Monitor" } ``` ## Parameters At least one parameter is required: | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ------------------- | | `upc_code_or_gtin` | string | No\* | UPC or GTIN barcode | | `product_name` | string | No\* | Product name | \*At least one of `upc_code_or_gtin` or `product_name` must be provided. UPC is more accurate. ## Request ```bash theme={null} POST /api/v2/products/check-eligibility ``` ## Response ```json theme={null} { "hsa_fsa_eligible": true, "message": "SIGIS verified - Medical device", "checked_at": "2025-10-18T14:30:00Z", "upc_code_or_gtin": "14567890123456", "product_name": "Digital Blood Pressure Monitor" } ``` ## Response Fields | Field | Type | Description | | ------------------ | --------- | ----------------------------------- | | `hsa_fsa_eligible` | boolean | Whether product is HSA/FSA eligible | | `message` | string | Explanation of eligibility status | | `checked_at` | timestamp | When eligibility was checked | ## Examples ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/products/check-eligibility \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "upc_code_or_gtin": "14567890123456", "product_name": "Digital Blood Pressure Monitor" }' ``` ## Not Eligible Response ```json theme={null} { "hsa_fsa_eligible": false, "message": "Product not found in SIGIS database. May not be HSA/FSA eligible or requires manual review.", "checked_at": "2025-10-18T14:30:00Z", "upc_code_or_gtin": "99999999999999", "product_name": "Regular Consumer Product" } ``` ## Dual-Purpose Products Some products require a Letter of Medical Necessity (LMN): ```json theme={null} { "hsa_fsa_eligible": true, "message": "Eligible with Letter of Medical Necessity (LMN). Customer must provide doctor's note.", "checked_at": "2025-10-18T14:30:00Z", "product_name": "Vitamin D Supplements" } ``` ## Rate Limits * **Test mode:** 100 requests/minute * **Live mode:** 1000 requests/minute For bulk checking, add 100ms delay between requests. ## Errors | Status Code | Error Code | Description | | ----------- | --------------------- | --------------------------------- | | 400 | `invalid_request` | Missing both UPC and product name | | 401 | `unauthorized` | Invalid or missing API key | | 422 | `validation_error` | Invalid UPC format | | 429 | `rate_limit_exceeded` | Too many requests | Example error: ```json theme={null} { "error": { "code": "invalid_request", "message": "Either upc_code_or_gtin or product_name must be provided" } } ``` ## Important Notes **Eligibility can change.** SIGIS database updates monthly. Re-check eligibility periodically for existing products. **Not a guarantee.** Eligibility check indicates SIGIS database status. Final determination depends on customer's HSA/FSA administrator. ## Related Endpoints * [Create Product](/api-reference/endpoint/create-product-v2) - POST /api/v2/products * [Get Product](/api-reference/endpoint/get-product-v2) - GET /api/v2/products/ * [Update Product](/api-reference/endpoint/update-product-v2) - PUT /api/v2/products/ * [List Products](/api-reference/endpoint/list-products-v2) - GET /api/v2/products ## Related Resources * [Product Object](/api-reference/objects/product) # Create Checkout Session Source: https://docs.withgale.com/api-reference/endpoint/create-checkout-v2 POST /api/v2/checkout Create a checkout session and get a hosted checkout URL # Create Checkout Session Create a checkout session for a customer. Returns a checkout URL where the customer completes payment on Gale's hosted page. Gale handles card collection, eligibility detection, and LMN flow automatically. All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ## Endpoint ``` POST /api/v2/checkout ``` ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Request Body ```json theme={null} { "reference_id": "your-order-123", "customer": { "email": "jane@example.com", "first_name": "Jane", "last_name": "Doe", "phone": "+1-555-123-4567" }, "line_items": [ { "product_id": "BP-MONITOR-001", "name": "Digital Blood Pressure Monitor", "image_url": "https://yourcdn.com/bp-monitor.jpg", "price": 4995, "quantity": 1, "currency": "USD" }, { "product_id": "VITAMINS-030", "name": "Daily Multivitamins (30-day)", "image_url": "https://yourcdn.com/vitamins.jpg", "price": 1999, "quantity": 2, "currency": "USD" } ], "shipping_info": { "address_line_1": "123 Main St", "address_line_2": "Apt 4B", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" }, "billing_info": { "address_line_1": "123 Main St", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" }, "shipping": 500, "tax": 410, "discount": 0, "success_url": "https://yoursite.com/order/success", "failure_url": "https://yoursite.com/order/failed", "metadata": { "platform": "CUSTOM", "webhook_url": "https://your-server.com/webhooks/gale" } } ``` ## Parameters | Parameter | Type | Required | Description | | :----------------------------- | :------ | :------- | :------------------------------------------------ | | `reference_id` | string | Yes | Your order or cart ID for correlation | | `customer` | object | Yes | Customer information | | `customer.email` | string | Yes | Customer email | | `customer.first_name` | string | Yes | Customer first name | | `customer.last_name` | string | Yes | Customer last name | | `customer.phone` | string | No | Customer phone number | | `line_items` | array | Yes | Products being purchased | | `line_items[].product_id` | string | Yes | Your product ID (must match synced product) | | `line_items[].name` | string | Yes | Product name | | `line_items[].image_url` | string | No | Product image URL | | `line_items[].price` | integer | Yes | Unit price in cents | | `line_items[].quantity` | integer | Yes | Quantity | | `line_items[].currency` | string | Yes | Currency code (e.g., `"USD"`) | | `shipping_info` | object | Yes | Shipping address | | `shipping_info.address_line_1` | string | Yes | Address line 1 | | `shipping_info.address_line_2` | string | No | Address line 2 | | `shipping_info.city` | string | Yes | City | | `shipping_info.state` | string | Yes | State | | `shipping_info.postal_code` | string | Yes | ZIP/Postal code | | `shipping_info.country` | string | Yes | Country code (e.g., `"US"`) | | `billing_info` | object | No | Billing address (defaults to shipping if omitted) | | `billing_info.address_line_1` | string | Yes | Address line 1 | | `billing_info.address_line_2` | string | No | Address line 2 | | `billing_info.city` | string | Yes | City | | `billing_info.state` | string | Yes | State | | `billing_info.postal_code` | string | Yes | ZIP/Postal code | | `billing_info.country` | string | Yes | Country code (e.g., `"US"`) | | `shipping` | integer | No | Shipping amount in cents | | `tax` | integer | No | Tax amount in cents | | `discount` | integer | No | Discount amount in cents | | `success_url` | string | Yes | Redirect URL after successful payment | | `failure_url` | string | Yes | Redirect URL if payment fails or customer cancels | | `metadata` | object | No | Custom key-value pairs | | `metadata.webhook_url` | string | No | URL to receive payment webhooks (recommended) | `product_id` is **your** product identifier — the one you supplied when syncing products (`merchant_product_id`), not the numeric id displayed in the dashboard. Line items that don't match a synced product are accepted as **pass-through items and treated as non-HSA/FSA-eligible**, so an unmatched id silently produces a regular (non-eligible) checkout. Your own product ids are identical across test and live, so the same integration carries to go-live unchanged. ## Response ```json theme={null} { "success": true, "message": "Checkout created", "data": { "checkout_id": "cs_a1b2c3...", "checkout_url": "https://checkout.withgale.com/checkout/cs_a1b2c3...?token=...", "reference_id": "order-789", "status": "open", "type": "eligible", "customer": { "email": "jane@example.com", "first_name": "Jane", "last_name": "Doe" }, "line_items": [ ... ], "amounts": { "subtotal": 4995, "hsa_amount": 4995, "regular_amount": 0, "shipping": 995, "tax": 410, "discount": 0, "total": 6400 }, "success_url": "https://yourapp.com/checkout/success", "failure_url": "https://yourapp.com/checkout/cancel", "expires_at": "2026-02-26T14:30:00Z", "created_at": "2026-02-26T13:30:00Z" }, "metadata": { "request_id": "..." } } ``` Fields are returned under `data`. | Field | Type | Description | | :------------------ | :-------- | :------------------------------------------------------------------------------------------------------- | | `data.checkout_id` | string | Unique checkout session identifier (`cs_...`) | | `data.checkout_url` | string | Hosted checkout page URL (includes access token) — redirect customer here | | `data.status` | string | Session status (`open`) | | `data.type` | string | Cart type (`eligible`, `split`, `regular`) | | `data.amounts` | object | All amounts in cents: `subtotal`, `hsa_amount`, `regular_amount`, `shipping`, `tax`, `discount`, `total` | | `data.expires_at` | timestamp | When session expires | ## Example ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/checkout \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "reference_id": "order-789", "customer": { "email": "jane@example.com", "first_name": "Jane", "last_name": "Doe" }, "line_items": [ { "product_id": "BP-MONITOR-001", "name": "Digital Blood Pressure Monitor", "price": 4995, "quantity": 1, "currency": "USD" } ], "shipping_info": { "address_line_1": "123 Main St", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" }, "shipping": 500, "tax": 410, "success_url": "https://yoursite.com/order/success", "failure_url": "https://yoursite.com/order/failed" }' ``` ### Subscription Checkout Beta ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/checkout \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "reference_id": "membership-456", "customer": { "email": "member@example.com", "first_name": "Jane", "last_name": "Doe" }, "line_items": [ { "product_id": "MEMBERSHIP-PREMIUM", "name": "Premium Membership", "price": 9900, "quantity": 1, "currency": "USD" } ], "payment_type": "subscription", "subscription": { "interval": "monthly", "trial_period_days": 14 }, "success_url": "https://yoursite.com/welcome", "failure_url": "https://yoursite.com/pricing" }' ``` ## Checkout Types Gale automatically determines the checkout type based on product eligibility: | Type | Description | | :--------- | :------------------------------------- | | `eligible` | All items are HSA/FSA eligible | | `split` | Mix of eligible and non-eligible items | | `regular` | No items are eligible | You don't need to specify the type — Gale handles this automatically based on the products in the checkout. ## Checkout Status | Status | Description | | :-------- | :------------------------------------------ | | `open` | Checkout session created, awaiting customer | | `paid` | Customer completed payment, order created | | `expired` | Session expired (24 hours) | ## Webhooks When payment completes, you receive an `order.created` webhook: ```json theme={null} { "type": "order.created", "data": { "id": "ord_abc123", "checkout_id": "01HXYZ...", "reference_id": "your-order-123", "status": "completed", "customer": { "email": "jane@example.com" } } } ``` See [Webhooks Reference](/developer-manual/webhooks) for setup and all event types. ## Errors | Status Code | Error Code | Description | | :---------- | :----------------- | :---------------------------- | | 400 | `bad_request` | Missing or invalid parameters | | 401 | `unauthorized` | Invalid API key | | 422 | `validation_error` | Field validation failed | See [Error Reference](/developer-manual/errors) for details. ## Related * [Get Checkout Session](/api-reference/endpoint/get-checkout) * [Checkout Session Object](/api-reference/objects/checkout-session) * [Checkout Integration Guide](/integration/checkout) * [Order Object](/api-reference/objects/order) * [Webhooks](/developer-manual/webhooks) # Create Payment Link Source: https://docs.withgale.com/api-reference/endpoint/create-payment-link POST /api/v2/payment-links Generate a payment link for one-time or recurring payments # Create Payment Link Generate a payment link that customers can use to complete checkout. Payment links automatically handle HSA/FSA eligibility detection and can be used for both one-time and recurring payments. All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Request Body ```json theme={null} { "amount": 4995, "currency": "USD", "description": "Premium Blood Pressure Monitor", "customer": { "email": "customer@example.com", "first_name": "Jane", "last_name": "Doe" }, "products": [ { "merchant_product_id": "BP-MONITOR-001", "name": "Blood Pressure Monitor", "quantity": 1, "price": 4995 } ], "payment_type": "one_time", "success_url": "https://yoursite.com/success", "cancel_url": "https://yoursite.com/cancel", "metadata": { "order_id": "ORD-12345" } } ``` ## Parameters | Parameter | Type | Required | Description | | :------------------------------- | :-------- | :------- | :------------------------------------------------- | | `amount` | integer | Yes | Total amount in cents | | `currency` | string | Yes | ISO 4217 currency code (e.g., `"USD"`) | | `description` | string | Yes | Description of the payment | | `customer` | object | No | Pre-fill customer information | | `customer.email` | string | No | Customer email | | `customer.first_name` | string | No | Customer first name | | `customer.last_name` | string | No | Customer last name | | `products` | array | No | Product line items | | `products[].merchant_product_id` | string | Yes\* | Your product reference ID | | `products[].name` | string | Yes\* | Product name | | `products[].quantity` | integer | Yes\* | Quantity | | `products[].price` | integer | Yes\* | Unit price in cents | | `payment_type` | enum | No | `one_time` or `subscription` (default: `one_time`) | | `subscription` | object | No | Required if `payment_type` is `subscription` | | `subscription.interval` | enum | Yes\* | `daily`, `weekly`, `monthly`, `yearly` | | `subscription.interval_count` | integer | No | Number of intervals (default: 1) | | `subscription.trial_period_days` | integer | No | Free trial days | | `success_url` | string | Yes | Redirect URL after successful payment | | `cancel_url` | string | No | Redirect URL if customer cancels | | `metadata` | object | No | Custom key-value pairs | | `expires_at` | timestamp | No | When link expires (default: 15 days) | ## Response ```json theme={null} { "id": "plink_abc123xyz", "url": "https://checkout.withgale.com/pay/plink_abc123xyz", "amount": 4995, "currency": "USD", "description": "Premium Blood Pressure Monitor", "payment_type": "one_time", "status": "active", "customer": { "email": "customer@example.com", "first_name": "Jane", "last_name": "Doe" }, "products": [ { "merchant_product_id": "BP-MONITOR-001", "name": "Blood Pressure Monitor", "quantity": 1, "price": 4995, "hsa_fsa_eligible": true } ], "success_url": "https://yoursite.com/success", "cancel_url": "https://yoursite.com/cancel", "metadata": { "order_id": "ORD-12345" }, "expires_at": "2026-03-12T14:30:00Z", "created_at": "2026-02-25T14:30:00Z" } ``` ## Example ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/payment-links \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 2995, "currency": "USD", "description": "Digital Thermometer", "products": [ { "merchant_product_id": "THERM-001", "name": "Digital Thermometer", "quantity": 1, "price": 2995 } ], "success_url": "https://yoursite.com/success" }' ``` ## Subscription Intervals | Interval | `interval_count` | Billing Frequency | | :-------- | :--------------- | :------------------------- | | `monthly` | 1 | Every month | | `monthly` | 3 | Every 3 months (quarterly) | | `weekly` | 1 | Every week | | `weekly` | 2 | Every 2 weeks (bi-weekly) | | `yearly` | 1 | Every year | ## Payment Link Status | Status | Description | | :---------- | :---------------------------------------- | | `active` | Link is active and ready for payment | | `expired` | Link has expired (past `expires_at` time) | | `paid` | Payment completed successfully | | `cancelled` | Link was cancelled | ## Webhooks When a customer completes payment on a payment link, Gale sends webhook events to your registered endpoints: * `order.completed` — Payment captured successfully * `order.failed` — Payment failed or declined See [Webhooks Reference](/developer-manual/webhooks) for payload examples and setup instructions. ## Errors | Status Code | Error Code | Description | | :---------- | :----------------- | :--------------------------------------- | | 400 | `invalid_request` | Missing or invalid parameters | | 400 | `invalid_amount` | Amount must be positive integer in cents | | 401 | `unauthorized` | Invalid or missing API key | | 422 | `validation_error` | Field validation failed | ## Related * [Get Payment Link](/api-reference/endpoint/get-payment-link) * [List Payment Links](/api-reference/endpoint/list-payment-links) * [Cancel Payment Link](/api-reference/endpoint/cancel-payment-link) * [Payment Links Integration Guide](/integration/payment-links) # Create Product Source: https://docs.withgale.com/api-reference/endpoint/create-product-v2 POST /api/v2/products Add a new product to your catalog with automatic HSA/FSA eligibility detection # Create Product Create a new product in your catalog. Gale automatically checks HSA/FSA eligibility via the SIGIS database using the product's UPC code or name. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Request Body ```json theme={null} { "name": "Digital Blood Pressure Monitor", "tagline": "FDA-approved automatic BP monitor", "description": "Clinically validated blood pressure monitor with Bluetooth connectivity. Features automatic inflation, irregular heartbeat detection, and memory for 200 readings. Includes carrying case and 4 AAA batteries.", "price": 4995, "currency": "USD", "merchant_product_id": "BP-MONITOR-001", "upc_code_or_gtin": "14567890123456", "images": [ "https://cdn.example.com/bp-monitor-1.jpg", "https://cdn.example.com/bp-monitor-2.jpg" ], "metadata": { "category": "medical_devices", "brand": "HealthTech Pro", "weight": "0.5 lbs" } } ``` ## Parameters > All monetary amounts are integers in cents (e.g., 4995 = \$49.95). | Parameter | Type | Required | Description | | --------------------- | ------- | -------- | ------------------------------------------------ | | `name` | string | Yes | Product name (max 255 chars) | | `tagline` | string | No | Short description (max 255 chars) | | `description` | string | No | Full product description (supports markdown) | | `price` | integer | Yes | Price in cents (e.g., 4995 = \$49.95) | | `currency` | string | Yes | ISO 4217 currency code (e.g., "USD") | | `merchant_product_id` | string | No | Your reference ID for this product | | `upc_code_or_gtin` | string | No | UPC or GTIN barcode (used for eligibility check) | | `images` | array | No | Array of image URLs (max 10) | | `metadata` | object | No | Custom key-value pairs | ## Response ```json theme={null} { "id": 12345, "name": "Digital Blood Pressure Monitor", "tagline": "FDA-approved automatic BP monitor", "description": "Clinically validated blood pressure monitor with Bluetooth connectivity. Features automatic inflation, irregular heartbeat detection, and memory for 200 readings. Includes carrying case and 4 AAA batteries.", "price": 4995, "currency": "USD", "merchant_product_id": "BP-MONITOR-001", "upc_code_or_gtin": "14567890123456", "status": "active", "eligibility": { "hsa_fsa_eligible": true, "message": "SIGIS verified - Medical device" }, "images": [ { "id": 567, "url": "https://cdn.example.com/bp-monitor-1.jpg", "is_primary": true }, { "id": 568, "url": "https://cdn.example.com/bp-monitor-2.jpg", "is_primary": false } ], "metadata": { "category": "medical_devices", "brand": "HealthTech Pro", "weight": "0.5 lbs" }, "created_at": "2025-10-18T14:30:00Z", "updated_at": "2025-10-18T14:30:00Z" } ``` See [Product Object](/api-reference/objects/product) for complete field descriptions. ## Examples ### Basic Product ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/products \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Digital Thermometer", "description": "Fast and accurate digital thermometer", "price": 2995, "currency": "USD" }' ``` ### Complete Product with UPC ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/products \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Premium Blood Pressure Monitor", "tagline": "Professional-grade BP monitoring", "description": "Clinically validated automatic blood pressure monitor with advanced features.", "price": 4995, "currency": "USD", "merchant_product_id": "BP-MONITOR-PRO", "upc_code_or_gtin": "14567890123456", "images": [ "https://cdn.example.com/bp-monitor-1.jpg", "https://cdn.example.com/bp-monitor-2.jpg" ], "metadata": { "category": "medical_devices", "brand": "HealthTech", "features": "bluetooth,memory,irregular_detection" } }' ``` ## Webhooks Product creation triggers the following webhook event: ```json theme={null} { "type": "product.created", "data": { "id": 12345, "name": "Digital Blood Pressure Monitor", "eligibility": { "hsa_fsa_eligible": true, "message": "SIGIS verified - Medical device" } } } ``` See [Webhooks Reference](/developer-manual/webhooks) for details. ## Errors | Status Code | Error Code | Description | | ----------- | --------------------- | ------------------------------ | | 400 | `invalid_request` | Missing or invalid parameters | | 400 | `invalid_price` | Price must be positive integer | | 400 | `invalid_currency` | Unsupported currency code | | 401 | `unauthorized` | Invalid or missing API key | | 422 | `validation_error` | Field validation failed | | 429 | `rate_limit_exceeded` | Too many requests | Example error: ```json theme={null} { "error": { "code": "validation_error", "message": "Invalid product data", "details": [ { "field": "name", "message": "Name is required" }, { "field": "price", "message": "Price must be a positive integer" } ] } } ``` ## Related * [Get Product](/api-reference/endpoint/get-product-v2) * [Update Product](/api-reference/endpoint/update-product-v2) * [List Products](/api-reference/endpoint/list-products-v2) * [Check Eligibility](/api-reference/endpoint/check-eligibility) * [Product Object](/api-reference/objects/product) # Create Refund Source: https://docs.withgale.com/api-reference/endpoint/create-refund POST /api/v2/refunds Refund a completed order (full or partial) # Create Refund Issue a full or partial refund for a completed order. Refunds are processed back to the original payment method (HSA/FSA card or regular card). ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Request Body ```json theme={null} { "order_id": "ord_abc123xyz", "amount": 4235, "reason": "customer_request", "notes": "Customer requested refund due to delayed shipping", "metadata": { "support_ticket": "TICKET-12345" } } ``` All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ## Parameters | Parameter | Type | Required | Description | | ---------- | ------- | -------- | ------------------------------------------------ | | `order_id` | string | Yes | ID of the order to refund | | `amount` | integer | No | Amount to refund in cents (omit for full refund) | | `reason` | enum | Yes | Refund reason (see below) | | `notes` | string | No | Additional notes (max 500 chars) | | `metadata` | object | No | Custom key-value pairs | **Finding Order ID**: You can get the `order_id` from: * Webhook event data (`order.created` event) * Order API response (`GET /api/v2/orders`) * Your database (store it when receiving `order.created` webhook) * Dashboard order details ## Refund Reasons | Reason | Description | Example Use Case | | --------------------- | ------------------------- | --------------------------------------- | | `customer_request` | Customer requested refund | Customer changed mind, no longer needed | | `duplicate` | Duplicate payment | Customer accidentally paid twice | | `fraudulent` | Fraudulent order | Detected fraudulent transaction | | `product_unavailable` | Product out of stock | Item went out of stock after order | | `damaged_product` | Product arrived damaged | Item damaged during shipping | | `wrong_product` | Wrong product shipped | Fulfillment error, sent wrong item | | `other` | Other reason | Any other reason (explain in `notes`) | ## Request ```bash theme={null} POST /api/v2/refunds ``` ## Response ```json theme={null} { "success": true, "message": "Refund created", "data": { "id": "re_3Toy...", "order_id": "ord_abc123xyz", "amount": 4235, "reason": "customer_request", "notes": "Customer requested refund due to delayed shipping", "status": "succeeded", "refund_breakdown": { "hsa_amount": 2995, "regular_amount": 1240 }, "refunds": [ { "refund_id": "re_3Toy...", "success": true } ], "metadata": { "support_ticket": "TICKET-12345" }, "created_at": "2026-07-23T16:00:00Z" }, "metadata": { "request_id": "..." } } ``` Fields are returned under `data`. ## Response Fields | Field | Type | Description | | ------------------ | ------ | --------------------------------------------------------------- | | `id` | string | Processor refund reference | | `status` | enum | Refund status: `pending`, `succeeded`, `failed`, `cancelled` | | `refund_breakdown` | object | Refund amount per payment type (`hsa_amount`, `regular_amount`) | | `refunds` | array | Per-payment refund results | ## Examples ### Full Refund ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/refunds \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "order_id": "ord_abc123xyz", "reason": "customer_request", "notes": "Customer not satisfied with product" }' ``` ### Partial Refund ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/refunds \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "order_id": "ord_abc123xyz", "amount": 1000, "reason": "damaged_product", "notes": "Partial refund for damaged item" }' ``` ## Refund Status | Status | Description | | ----------- | ----------------------------- | | `pending` | Refund initiated, processing | | `succeeded` | Refund processed successfully | | `failed` | Refund failed | | `cancelled` | Refund cancelled | ## Split Card Refunds (Automatic) **Gale automatically handles split card refunds.** You don't need to specify how much goes to each card. When an order is paid with both HSA/FSA and regular card: ```json theme={null} { "order": { "amounts": { "hsa_amount": 4995, // Paid with HSA/FSA card "regular_amount": 895, // Paid with regular card "total": 5890 } } } ``` ### Full Refund Just provide the `order_id` - Gale splits automatically: ```bash theme={null} POST /api/v2/refunds { "order_id": "ord_abc123", "reason": "customer_request" } ``` **Response shows the breakdown:** ```json theme={null} { "id": "ref_xyz789", "amount": 5890, "refund_breakdown": { "hsa_amount": 4995, // Refunded to HSA/FSA card "regular_amount": 895 // Refunded to regular card } } ``` ### Partial Refund Gale splits proportionally based on the original payment ratio: ```bash theme={null} POST /api/v2/refunds { "order_id": "ord_abc123", "amount": 2945, // Partial refund "reason": "damaged_product" } ``` **Response:** ```json theme={null} { "id": "ref_xyz789", "amount": 2945, "refund_breakdown": { "hsa_amount": 2498, // ~84.7% (same ratio as original) "regular_amount": 447 // ~15.3% } } ``` **Key Points:** * You only specify total `amount` * Gale calculates the split automatically * Each amount refunds to its original payment method * Response shows the breakdown for your records ## Webhooks Refunds trigger the following webhook events: ```json theme={null} { "type": "refund.created", "data": { "id": "ref_xyz789", "order_id": "ord_abc123xyz", "amount": 4235, "status": "pending" } } ``` ```json theme={null} { "type": "refund.succeeded", "data": { "id": "ref_xyz789", "order_id": "ord_abc123xyz", "status": "succeeded", "processed_at": "2025-10-18T16:05:00Z" } } ``` See [Webhooks Reference](/developer-manual/webhooks) for all refund events. ## Errors | Status Code | Error Code | Description | | ----------- | ------------------ | --------------------------------- | | 400 | `invalid_amount` | Refund amount exceeds order total | | 400 | `already_refunded` | Order already fully refunded | | 400 | `invalid_state` | Order not in refundable state | | 401 | `unauthorized` | Invalid or missing API key | | 404 | `not_found` | Order not found | | 422 | `validation_error` | Field validation failed | ### Amount Too Large ```json theme={null} { "error": { "code": "invalid_amount", "message": "Refund amount exceeds order total", "details": { "requested": 10000, "maximum": 4235 } } } ``` ### Already Refunded ```json theme={null} { "error": { "code": "already_refunded", "message": "Order has already been fully refunded" } } ``` ### Invalid State ```json theme={null} { "error": { "code": "invalid_state", "message": "Cannot refund order with payment status 'pending'" } } ``` ## Refund Timeline | Payment Method | Typical Arrival Time | | -------------- | -------------------- | | HSA/FSA Card | 5-10 business days | | Regular Card | 5-10 business days | **Note:** Actual timing depends on the customer's card issuer. ## Important Notes **Refunds cannot be undone.** Once a refund is processed, it cannot be reversed. Double-check order ID and amount before submitting. **Partial refunds are supported.** You can issue multiple partial refunds up to the order total. The remaining refundable amount is tracked automatically. ## Related Endpoints * [Get Order](/api-reference/endpoint/get-order) - GET /api/v2/orders/ * [List Orders](/api-reference/endpoint/list-orders) - GET /api/v2/orders ## Related Resources * [Order Object](/api-reference/objects/order) * [Webhooks Reference](/developer-manual/webhooks) # Create Subscription Source: https://docs.withgale.com/api-reference/endpoint/create-subscription POST /api/v2/subscriptions Create a recurring payment subscription # Create Subscription Create a new subscription for recurring payments. Subscriptions are typically created after a customer completes checkout via a payment link with `payment_type: "subscription"`. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Request Body ```json theme={null} { "customer_id": "cus_xyz789", "product_id": 12345, "amount_cents": 9900, "currency": "USD", "interval": "monthly", "interval_count": 1, "trial_period_days": 7, "payment_method_id": "pm_abc123", "metadata": { "tier": "premium", "source": "website" } } ``` ## Parameters | Parameter | Type | Required | Description | | --------------------- | ------- | -------- | ----------------------------------------- | | `customer_id` | string | Yes | Customer ID | | `product_id` | integer | No | Associated product ID | | `merchant_product_id` | string | No | Your product reference ID | | `amount_cents` | integer | Yes | Recurring charge amount in cents | | `currency` | string | Yes | ISO 4217 currency code (e.g., "USD") | | `interval` | enum | Yes | `daily`, `weekly`, `monthly`, or `yearly` | | `interval_count` | integer | No | Number of intervals (default: 1) | | `trial_period_days` | integer | No | Free trial duration in days | | `payment_method_id` | string | Yes | ID of stored payment method | | `metadata` | object | No | Custom key-value pairs | ## Response ```json theme={null} { "id": "sub_abc123xyz", "customer_id": "cus_xyz789", "status": "trialing", "product_id": 12345, "merchant_product_id": "MEMBERSHIP-PREMIUM", "amount_cents": 9900, "currency": "USD", "interval": "monthly", "interval_count": 1, "trial_period_days": 7, "trial_end": "2025-10-25T14:30:00Z", "current_period_start": "2025-10-18T14:30:00Z", "current_period_end": "2025-11-18T14:30:00Z", "next_billing_date": "2025-10-25T14:30:00Z", "cancel_at_period_end": false, "payment_method": { "type": "hsa_fsa_card", "last4": "1111", "brand": "visa" }, "metadata": { "tier": "premium", "source": "website" }, "created_at": "2025-10-18T14:30:00Z", "updated_at": "2025-10-18T14:30:00Z" } ``` See [Subscription Object](/api-reference/objects/subscription) for complete field descriptions. ## Examples ### Monthly Subscription with Trial ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/subscriptions \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cus_xyz789", "product_id": 12345, "amount_cents": 9900, "currency": "USD", "interval": "monthly", "trial_period_days": 14, "payment_method_id": "pm_abc123" }' ``` ### Annual Subscription ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/subscriptions \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cus_xyz789", "product_id": 12346, "amount_cents": 99900, "currency": "USD", "interval": "yearly", "payment_method_id": "pm_abc123" }' ``` ### Bi-weekly Subscription ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/subscriptions \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cus_xyz789", "merchant_product_id": "COACHING-BIWEEKLY", "amount_cents": 4900, "currency": "USD", "interval": "weekly", "interval_count": 2, "payment_method_id": "pm_abc123" }' ``` ### With JavaScript ```javascript theme={null} const createSubscription = async (customerId, planDetails) => { const response = await fetch('https://api.withgale.com/api/v2/subscriptions', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.GALE_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ customer_id: customerId, product_id: planDetails.productId, amount_cents: planDetails.price * 100, currency: 'USD', interval: planDetails.interval, trial_period_days: planDetails.trialDays, payment_method_id: planDetails.paymentMethodId, metadata: { tier: planDetails.tier, source: 'web_app' } }) }); if (!response.ok) { throw new Error('Failed to create subscription'); } const subscription = await response.json(); console.log(`Subscription created: ${subscription.id}`); console.log(`Status: ${subscription.status}`); console.log(`Next billing: ${subscription.next_billing_date}`); return subscription; }; // Usage const subscription = await createSubscription('cus_xyz789', { productId: 12345, price: 99, interval: 'monthly', trialDays: 7, paymentMethodId: 'pm_abc123', tier: 'premium' }); ``` ## Use Cases ### Membership Tiers ```javascript theme={null} // Create subscription based on selected tier const membershipTiers = { basic: { productId: 12345, amount_cents: 4900, features: 'basic_access' }, premium: { productId: 12346, amount_cents: 9900, features: 'unlimited_access,priority_support' }, elite: { productId: 12347, amount_cents: 19900, features: 'unlimited_access,priority_support,1on1_coaching' } }; const subscribeMember = async (customerId, tier, paymentMethodId) => { const plan = membershipTiers[tier]; return await createSubscription({ customer_id: customerId, product_id: plan.productId, amount_cents: plan.amount_cents, currency: 'USD', interval: 'monthly', trial_period_days: 14, payment_method_id: paymentMethodId, metadata: { tier: tier, features: plan.features } }); }; ``` ### Post-Checkout Subscription Creation ```javascript theme={null} // Create subscription after payment link is paid app.post('/webhooks/gale', async (req, res) => { const event = req.body; if (event.type === 'payment_link.paid') { const { payment_link, customer, payment_method } = event.data; // Check if this was a subscription payment link if (payment_link.payment_type === 'subscription') { const subscription = await createSubscription({ customer_id: customer.id, product_id: payment_link.product_id, amount_cents: payment_link.amount_cents, currency: payment_link.currency, interval: payment_link.subscription.interval, interval_count: payment_link.subscription.interval_count, trial_period_days: payment_link.subscription.trial_period_days, payment_method_id: payment_method.id }); console.log(`Subscription ${subscription.id} created for customer ${customer.email}`); } } res.status(200).send('OK'); }); ``` ### Upgrade/Downgrade Flow ```javascript theme={null} // Cancel old subscription and create new one const changeSubscriptionTier = async (customerId, oldSubId, newTier) => { // Cancel existing subscription at period end await cancelSubscription(oldSubId, { cancel_at_period_end: true }); // Create new subscription const newSub = await subscribeMember( customerId, newTier, paymentMethodId ); return { oldSubscription: oldSubId, newSubscription: newSub.id, effectiveDate: newSub.current_period_start }; }; ``` ## Webhooks Subscription creation triggers the following webhook event: ```json theme={null} { "type": "subscription.created", "data": { "id": "sub_abc123xyz", "customer_id": "cus_xyz789", "status": "trialing", "amount_cents": 9900, "interval": "monthly", "trial_end": "2025-10-25T14:30:00Z" } } ``` See [Webhooks Reference](/developer-manual/webhooks) for all subscription events. ## Errors | Status Code | Error Code | Description | | ----------- | ------------------------ | ----------------------------- | | 400 | `invalid_request` | Missing or invalid parameters | | 400 | `invalid_payment_method` | Payment method not valid | | 401 | `unauthorized` | Invalid or missing API key | | 404 | `customer_not_found` | Customer ID not found | | 422 | `validation_error` | Field validation failed | | 429 | `rate_limit_exceeded` | Too many requests | Example error: ```json theme={null} { "error": { "code": "validation_error", "message": "Invalid subscription parameters", "details": [ { "field": "amount_cents", "message": "Amount must be a positive integer" } ] } } ``` ## Best Practices Use trials to reduce friction for new subscribers Collect and verify payment method before trial ends Listen for subscription events for lifecycle management Track features and tiers in metadata ## Important Notes **Payment method required.** Even with a trial period, a valid payment method must be provided and will be verified during subscription creation. **HSA/FSA subscriptions.** Ensure recurring charges are for eligible products/services. Some HSA/FSA administrators restrict recurring charges. ## Related Endpoints * [Get Subscription](/api-reference/endpoint/get-subscription) - GET /api/v2/subscriptions/ * [List Subscriptions](/api-reference/endpoint/list-subscriptions) - GET /api/v2/subscriptions * [Update Subscription](/api-reference/endpoint/update-subscription) - PUT /api/v2/subscriptions/ * [Cancel Subscription](/api-reference/endpoint/cancel-subscription) - POST /api/v2/subscriptions//cancel ## Related Resources * [Subscription Object](/api-reference/objects/subscription) * [Webhooks Reference](/developer-manual/webhooks) * [Payment Links](/api-reference/endpoint/create-payment-link) # Get Checkout Status Source: https://docs.withgale.com/api-reference/endpoint/get-checkout GET /api/v2/checkout/{checkout_id} Retrieve the status and order details for a checkout session or payment link # Get Checkout Status Universal status polling endpoint for both checkout sessions and payment link flows. Use this when you need to check whether a payment has completed — for example, if a webhook was missed or you want to confirm status on page load. ## Endpoint ``` GET /api/v2/checkout/{checkout_id} ``` The `checkout_id` is the `cs_xxx` value returned when the cart was created. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Path Parameters | Parameter | Type | Description | | :------------ | :----- | :------------------------- | | `checkout_id` | string | The checkout ID (`cs_xxx`) | ## Response **Before payment (awaiting customer):** ```json theme={null} { "checkout_id": "cs_abc123", "status": "pending", "order": null } ``` **After successful payment:** ```json theme={null} { "checkout_id": "cs_abc123", "status": "completed", "order": { "id": "ord_01ABC...", "status": "completed", "is_recurring": false, "payment_status": "captured", "amount": 4995, "currency": "usd", "payment_link_id": "pl_abc123", "client_reference_id": "your-order-123", "reference_id": null, "customer": { "email": "jane@example.com", "name": "Jane Doe" }, "line_items": [ { "product_id": "PROD-001", "name": "Digital Thermometer", "quantity": 1, "unit_amount": 4995, "amount": 4995, "currency": "usd", "is_recurring": false } ], "created_at": "2026-03-31T12:00:00Z" } } ``` **After failed payment:** ```json theme={null} { "checkout_id": "cs_abc123", "status": "failed", "order": { "id": "ord_01DEF...", "status": "failed", "is_recurring": false, "payment_status": "failed", "amount": 4995, "currency": "usd", "customer": { "email": "jane@example.com", "name": "Jane Doe" }, "created_at": "2026-03-31T12:01:00Z" } } ``` ## Status Values | Status | Description | | :---------- | :------------------------------------ | | `pending` | Cart exists, no payment attempted yet | | `completed` | Payment captured successfully | | `failed` | Payment failed or declined | ## Example ```bash theme={null} curl https://api.withgale.com/api/v2/checkout/cs_abc123 \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ## Errors | Status Code | Error Code | Description | | :---------- | :------------- | :-------------------- | | 401 | `unauthorized` | Invalid API key | | 404 | `not_found` | Checkout ID not found | ## Related * [Webhooks](/developer-manual/webhooks) * [Order Object](/api-reference/objects/order) * [Create Checkout Session](/api-reference/endpoint/create-checkout-v2) # Get Order Source: https://docs.withgale.com/api-reference/endpoint/get-order GET /api/v2/orders/{id} Retrieve details about a specific order # Get Order Retrieve information about an order, including payment status, customer details, and line items. Orders are created when a checkout session or payment link is successfully paid. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Path Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | -------------------------------- | | `id` | string | Yes | Order ID (e.g., `ord_abc123xyz`) | ## Request ```bash theme={null} GET /api/v2/orders/{id} ``` ## Response > All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ```json theme={null} { "id": "ord_abc123xyz", "order_number": "ORD-2025-001234", "checkout_id": "01HXYZ...", "status": "completed", "payment_status": "captured", "receipt_url": "https://files.withgale.com/receipts/ord_abc123xyz.pdf?expires=...&signature=...", "customer": { "email": "jane@example.com", "first_name": "Jane", "last_name": "Doe", "phone": "+1-555-123-4567", "shipping_address": { "address_line_1": "123 Main St", "address_line_2": "Apt 4B", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" }, "billing_address": { "address_line_1": "123 Main St", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" } }, "line_items": [ { "id": "li_123", "product_id": "PROD-001", "name": "Digital Thermometer", "quantity": 1, "price": 2995, "hsa_fsa_eligible": true } ], "amounts": { "subtotal": 2995, "hsa_amount": 2995, "regular_amount": 0, "shipping": 995, "tax": 245, "discount": 0, "total": 4235 }, "payment": { "payment_id": "pay_xyz789", "payment_method": "hsa_fsa_card", "last4": "1111", "brand": "visa", "captured_at": "2025-10-18T15:30:00Z" }, "metadata": { "order_number": "ORDER-12345", "platform": "SHOPIFY" }, "created_at": "2025-10-18T15:00:00Z", "completed_at": "2025-10-18T15:30:00Z", "status_history": [ { "status": "pending", "timestamp": "2025-10-18T15:00:00Z", "reason": "Order created" }, { "status": "processing", "timestamp": "2025-10-18T15:01:00Z", "reason": "Payment processing" }, { "status": "completed", "timestamp": "2025-10-18T15:30:00Z", "reason": "Payment captured" } ] } ``` `receipt_url` is a temporary link to the order's receipt PDF. It is `null` until the receipt is generated and expires after one hour. Request the order again for a new link. ## Response Fields See [Order Object](/api-reference/objects/order) for complete field descriptions. ## Examples ### Basic Retrieval ```bash theme={null} curl https://api.withgale.com/api/v2/orders/ord_abc123xyz \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ## Order Status | Status | Description | | ------------ | ---------------------------------- | | `pending` | Order created, awaiting payment | | `processing` | Payment being processed | | `completed` | Payment successful, order complete | | `failed` | Payment failed | | `cancelled` | Order cancelled | ## Payment Status | Status | Description | | ------------ | ---------------------------------- | | `pending` | Payment not yet attempted | | `authorized` | Payment authorized (not captured) | | `captured` | Payment captured successfully | | `failed` | Payment failed | | `refunded` | Payment refunded (full or partial) | | `disputed` | Payment disputed/chargebacked | ## Errors | Status Code | Error Code | Description | | ----------- | --------------------- | -------------------------- | | 401 | `unauthorized` | Invalid or missing API key | | 404 | `not_found` | Order not found | | 429 | `rate_limit_exceeded` | Too many requests | Example error: ```json theme={null} { "error": { "code": "not_found", "message": "Order not found", "param": "id" } } ``` ## Related * [List Orders](/api-reference/endpoint/list-orders) * [Create Refund](/api-reference/endpoint/create-refund) * [Order Object](/api-reference/objects/order) * [Webhooks Reference](/developer-manual/webhooks) # Get Payment Link Source: https://docs.withgale.com/api-reference/endpoint/get-payment-link GET /api/v2/payment-links/{id} Retrieve details about a specific payment link # Get Payment Link Retrieve information about a payment link, including its current status and payment details. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Path Parameters | Parameter | Type | Required | Description | | :-------- | :----- | :------- | :---------------------------------------- | | `id` | string | Yes | Payment link ID (e.g., `plink_abc123xyz`) | ## Response > All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ```json theme={null} { "id": "plink_abc123xyz", "url": "https://checkout.withgale.com/pay/plink_abc123xyz", "amount": 4995, "currency": "USD", "description": "Premium Blood Pressure Monitor", "payment_type": "one_time", "status": "paid", "customer": { "email": "customer@example.com", "first_name": "Jane", "last_name": "Doe" }, "products": [ { "merchant_product_id": "BP-MONITOR-001", "name": "Blood Pressure Monitor", "quantity": 1, "price": 4995, "hsa_fsa_eligible": true } ], "order": { "id": "ord_xyz789", "status": "completed", "paid_at": "2026-02-25T15:30:00Z" }, "success_url": "https://yoursite.com/success", "cancel_url": "https://yoursite.com/cancel", "metadata": { "order_id": "ORD-12345" }, "expires_at": "2026-03-12T14:30:00Z", "created_at": "2026-02-25T14:30:00Z", "paid_at": "2026-02-25T15:30:00Z" } ``` ## Response Fields | Field | Type | Description | | :-------------- | :-------- | :------------------------------------------------- | | `id` | string | Unique payment link identifier | | `url` | string | Checkout URL for the payment link | | `amount` | integer | Total amount in cents | | `status` | enum | `active`, `paid`, `expired`, or `cancelled` | | `order` | object | Associated order (present when `status` is `paid`) | | `order.id` | string | Order ID | | `order.status` | enum | Order status | | `order.paid_at` | timestamp | When payment was completed | | `paid_at` | timestamp | When link was paid (null if not paid) | ## Example ```bash theme={null} curl https://api.withgale.com/api/v2/payment-links/plink_abc123xyz \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ## Payment Link Status | Status | Description | | :---------- | :------------------------------- | | `active` | Link is ready to accept payment | | `paid` | Payment completed, order created | | `expired` | Link expired without payment | | `cancelled` | Link was manually cancelled | ## Errors | Status Code | Error Code | Description | | :---------- | :------------- | :------------------------- | | 401 | `unauthorized` | Invalid or missing API key | | 404 | `not_found` | Payment link not found | ## Related * [Create Payment Link](/api-reference/endpoint/create-payment-link) * [List Payment Links](/api-reference/endpoint/list-payment-links) * [Cancel Payment Link](/api-reference/endpoint/cancel-payment-link) * [Order Object](/api-reference/objects/order) # Get Product Source: https://docs.withgale.com/api-reference/endpoint/get-product-v2 GET /api/v2/products/{id} Retrieve details about a specific product # Get Product Retrieve information about a product, including current HSA/FSA eligibility status. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Path Parameters | Parameter | Type | Required | Description | | --------- | ------- | -------- | ----------- | | `id` | integer | Yes | Product ID | ## Request ```bash theme={null} GET /api/v2/products/{id} ``` ## Response ```json theme={null} { "id": 12345, "name": "Digital Blood Pressure Monitor", "tagline": "FDA-approved automatic BP monitor", "description": "Clinically validated blood pressure monitor with Bluetooth connectivity. Features automatic inflation, irregular heartbeat detection, and memory for 200 readings. Includes carrying case and 4 AAA batteries.", "price": 4995, "currency": "USD", "merchant_product_id": "BP-MONITOR-001", "upc_code_or_gtin": "14567890123456", "status": "active", "eligibility": { "hsa_fsa_eligible": true, "message": "SIGIS verified - Medical device", "checked_at": "2025-10-18T14:30:00Z" }, "images": [ { "id": 567, "url": "https://cdn.example.com/bp-monitor-front.jpg", "is_primary": true }, { "id": 568, "url": "https://cdn.example.com/bp-monitor-side.jpg", "is_primary": false } ], "metadata": { "category": "medical_devices", "brand": "HealthTech Pro", "weight": "0.5 lbs" }, "created_at": "2025-10-18T14:30:00Z", "updated_at": "2025-10-18T14:30:00Z" } ``` All monetary amounts are integers in cents (e.g., 4995 = \$49.95). See [Product Object](/api-reference/objects/product) for complete field descriptions. ## Examples ```bash theme={null} curl https://api.withgale.com/api/v2/products/12345 \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ## Product Status | Status | Description | | ---------- | ----------------------------------- | | `active` | Available for sale | | `inactive` | Temporarily unavailable | | `archived` | Removed from catalog (soft deleted) | ## Errors | Status Code | Error Code | Description | | ----------- | --------------------- | -------------------------- | | 401 | `unauthorized` | Invalid or missing API key | | 404 | `not_found` | Product not found | | 429 | `rate_limit_exceeded` | Too many requests | Example error: ```json theme={null} { "error": { "code": "not_found", "message": "Product not found", "param": "id" } } ``` ## Related Endpoints * [Create Product](/api-reference/endpoint/create-product-v2) - POST /api/v2/products * [Update Product](/api-reference/endpoint/update-product-v2) - PUT /api/v2/products/ * [List Products](/api-reference/endpoint/list-products-v2) - GET /api/v2/products * [Check Eligibility](/api-reference/endpoint/check-eligibility) - POST /api/v2/products/check-eligibility ## Related Resources * [Product Object](/api-reference/objects/product) * [Payment Links](/api-reference/endpoint/create-payment-link) # Get Subscription Source: https://docs.withgale.com/api-reference/endpoint/get-subscription GET /api/v2/subscriptions/{id} Retrieve details about a specific subscription # Get Subscription Retrieve information about a subscription, including its current status and next billing date. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Path Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | --------------------------------------- | | `id` | string | Yes | Subscription ID (e.g., `sub_abc123xyz`) | ## Request ```bash theme={null} GET /api/v2/subscriptions/{id} ``` ## Response ```json theme={null} { "id": "sub_abc123xyz", "customer_id": "cus_xyz789", "status": "active", "product_id": 12345, "amount_cents": 9900, "currency": "USD", "interval": "monthly", "interval_count": 1, "current_period_start": "2025-10-18T14:30:00Z", "current_period_end": "2025-11-18T14:30:00Z", "next_billing_date": "2025-11-18T14:30:00Z", "cancel_at_period_end": false, "payment_method": { "type": "hsa_fsa_card", "last4": "1111", "brand": "visa" }, "created_at": "2025-10-18T14:30:00Z", "updated_at": "2025-10-18T14:30:00Z" } ``` See [Subscription Object](/api-reference/objects/subscription) for complete field descriptions. ## Examples ### Basic Retrieval ```bash theme={null} curl https://api.withgale.com/api/v2/subscriptions/sub_abc123xyz \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ### With JavaScript ```javascript theme={null} const getSubscription = async (subscriptionId) => { const response = await fetch( `https://api.withgale.com/api/v2/subscriptions/${subscriptionId}`, { headers: { 'Authorization': `Bearer ${process.env.GALE_API_KEY}` } } ); return await response.json(); }; // Check subscription status const sub = await getSubscription('sub_abc123xyz'); console.log(`Status: ${sub.status}`); console.log(`Next billing: ${sub.next_billing_date}`); ``` ## Related Endpoints * [Create Subscription](/api-reference/endpoint/create-subscription) * [List Subscriptions](/api-reference/endpoint/list-subscriptions) * [Cancel Subscription](/api-reference/endpoint/cancel-subscription) # List Orders Source: https://docs.withgale.com/api-reference/endpoint/list-orders GET /api/v2/orders Retrieve a list of all orders with optional filtering # List Orders Returns a paginated list of orders, ordered by creation date (most recent first). Supports filtering by status, customer, payment status, and date range. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Query Parameters | Parameter | Type | Description | | ---------------- | --------- | ----------------------------------------------------------------------------------------------- | | `limit` | integer | Number of results per page (default: 10, max: 100) | | `starting_after` | string | Cursor for pagination (order ID) | | `status` | enum | Filter by status: `pending`, `processing`, `completed`, `failed`, `cancelled` | | `payment_status` | enum | Filter by payment status: `pending`, `authorized`, `captured`, `failed`, `refunded`, `disputed` | | `customer_email` | string | Filter by customer email | | `created_after` | timestamp | Filter orders created after this date | | `created_before` | timestamp | Filter orders created before this date | ## Request ```bash theme={null} GET /api/v2/orders?limit=10&status=completed ``` ## Response > All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ```json theme={null} { "data": [ { "id": "ord_abc123", "order_number": "ORD-2025-001234", "status": "completed", "payment_status": "captured", "customer": { "email": "customer@example.com", "first_name": "Jane", "last_name": "Doe" }, "amounts": { "total": 4235, "hsa_amount": 2995, "regular_amount": 0 }, "payment": { "payment_method": "hsa_fsa_card", "last4": "1111" }, "created_at": "2025-10-18T15:00:00Z", "completed_at": "2025-10-18T15:30:00Z" }, { "id": "ord_def456", "order_number": "ORD-2025-001235", "status": "completed", "payment_status": "captured", "customer": { "email": "member@example.com", "first_name": "John", "last_name": "Smith" }, "amounts": { "total": 9900, "hsa_amount": 9900, "regular_amount": 0 }, "payment": { "payment_method": "hsa_fsa_card", "last4": "2222" }, "created_at": "2025-10-17T10:00:00Z", "completed_at": "2025-10-17T10:15:00Z" } ], "has_more": true, "next_cursor": "ord_def456" } ``` ## Response Fields | Field | Type | Description | | ------------- | ------- | ---------------------------------------------- | | `data` | array | Array of order objects | | `has_more` | boolean | Whether more results are available | | `next_cursor` | string | Cursor for next page (use as `starting_after`) | ## Examples ### List All Orders ```bash theme={null} curl https://api.withgale.com/api/v2/orders \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ### Filter by Status ```bash theme={null} curl "https://api.withgale.com/api/v2/orders?status=completed&limit=50" \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ### Filter by Payment Status ```bash theme={null} curl "https://api.withgale.com/api/v2/orders?payment_status=captured" \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ### Filter by Customer ```bash theme={null} curl "https://api.withgale.com/api/v2/orders?customer_email=jane@example.com" \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ## Errors | Status Code | Error Code | Description | | ----------- | --------------------- | -------------------------- | | 400 | `invalid_request` | Invalid query parameters | | 401 | `unauthorized` | Invalid or missing API key | | 429 | `rate_limit_exceeded` | Too many requests | Example error: ```json theme={null} { "error": { "code": "invalid_request", "message": "Invalid status value", "param": "status" } } ``` ## Related * [Get Order](/api-reference/endpoint/get-order) * [Create Refund](/api-reference/endpoint/create-refund) * [Order Object](/api-reference/objects/order) * [Webhooks Reference](/developer-manual/webhooks) # List Payment Links Source: https://docs.withgale.com/api-reference/endpoint/list-payment-links GET /api/v2/payment-links Retrieve a list of all payment links with optional filtering # List Payment Links Returns a paginated list of payment links, ordered by creation date (most recent first). Supports filtering by status, customer, and date range. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Query Parameters | Parameter | Type | Description | | :--------------- | :-------- | :--------------------------------------------------------- | | `limit` | integer | Number of results per page (default: 10, max: 100) | | `starting_after` | string | Cursor for pagination (payment link ID) | | `status` | enum | Filter by status: `active`, `paid`, `expired`, `cancelled` | | `customer_email` | string | Filter by customer email | | `payment_type` | enum | Filter by type: `one_time`, `subscription` | | `created_after` | timestamp | Filter links created after this date | | `created_before` | timestamp | Filter links created before this date | ## Response > All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ```json theme={null} { "data": [ { "id": "plink_abc123", "url": "https://checkout.withgale.com/pay/plink_abc123", "amount": 4995, "currency": "USD", "description": "Blood Pressure Monitor", "payment_type": "one_time", "status": "paid", "customer": { "email": "customer@example.com" }, "order": { "id": "ord_xyz789" }, "created_at": "2026-02-25T14:30:00Z", "paid_at": "2026-02-25T15:30:00Z" } ], "has_more": true, "next_cursor": "plink_abc123" } ``` ## Response Fields | Field | Type | Description | | :------------ | :------ | :--------------------------------------------- | | `data` | array | Array of payment link objects | | `has_more` | boolean | Whether more results are available | | `next_cursor` | string | Cursor for next page (use as `starting_after`) | ## Examples ### List All ```bash theme={null} curl https://api.withgale.com/api/v2/payment-links \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ### Filter by Status ```bash theme={null} curl "https://api.withgale.com/api/v2/payment-links?status=paid&limit=50" \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ### Filter by Customer ```bash theme={null} curl "https://api.withgale.com/api/v2/payment-links?customer_email=jane@example.com" \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ## Errors | Status Code | Error Code | Description | | :---------- | :---------------- | :------------------------- | | 400 | `invalid_request` | Invalid query parameters | | 401 | `unauthorized` | Invalid or missing API key | ## Related * [Create Payment Link](/api-reference/endpoint/create-payment-link) * [Get Payment Link](/api-reference/endpoint/get-payment-link) * [Cancel Payment Link](/api-reference/endpoint/cancel-payment-link) # List Products Source: https://docs.withgale.com/api-reference/endpoint/list-products-v2 GET /api/v2/products Retrieve a list of all products with optional filtering # List Products Returns a paginated list of products, ordered by creation date (most recent first). Supports filtering by status, eligibility, and more. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Query Parameters | Parameter | Type | Description | | --------------------- | --------- | -------------------------------------------------- | | `limit` | integer | Number of results per page (default: 10, max: 100) | | `starting_after` | integer | Cursor for pagination (product ID) | | `status` | enum | Filter by status: `active`, `inactive`, `archived` | | `hsa_fsa_eligible` | boolean | Filter by HSA/FSA eligibility (true/false) | | `merchant_product_id` | string | Filter by your product ID | | `created_after` | timestamp | Filter products created after this date | | `created_before` | timestamp | Filter products created before this date | ## Request ```bash theme={null} GET /api/v2/products?limit=10&status=active ``` ## Response ```json theme={null} { "data": [ { "id": 12345, "name": "Digital Blood Pressure Monitor", "tagline": "FDA-approved automatic BP monitor", "price": 4995, "currency": "USD", "merchant_product_id": "BP-MONITOR-001", "status": "active", "eligibility": { "hsa_fsa_eligible": true, "message": "SIGIS verified - Medical device" }, "images": [ { "id": 567, "url": "https://cdn.example.com/bp-monitor.jpg", "is_primary": true } ], "created_at": "2025-10-18T14:30:00Z" }, { "id": 12346, "name": "Digital Thermometer", "price": 2995, "currency": "USD", "merchant_product_id": "THERM-001", "status": "active", "eligibility": { "hsa_fsa_eligible": true, "message": "SIGIS verified - Medical device" }, "created_at": "2025-10-17T10:00:00Z" } ], "has_more": true, "next_cursor": 12346 } ``` ## Response Fields | Field | Type | Description | | ------------- | ------- | ---------------------------------------------- | | `data` | array | Array of product objects | | `has_more` | boolean | Whether more results are available | | `next_cursor` | integer | Cursor for next page (use as `starting_after`) | ## Examples ### List All Products ```bash theme={null} curl https://api.withgale.com/api/v2/products \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ### Filter by Status ```bash theme={null} curl "https://api.withgale.com/api/v2/products?status=active&limit=50" \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ### Filter by HSA/FSA Eligibility ```bash theme={null} curl "https://api.withgale.com/api/v2/products?hsa_fsa_eligible=true" \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ### Pagination Use cursor-based pagination for large datasets. Pass the `next_cursor` value from the previous response as the `starting_after` parameter: ```bash theme={null} curl "https://api.withgale.com/api/v2/products?limit=100&starting_after=12345" \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ## Errors | Status Code | Error Code | Description | | ----------- | --------------------- | -------------------------- | | 400 | `invalid_request` | Invalid query parameters | | 401 | `unauthorized` | Invalid or missing API key | | 429 | `rate_limit_exceeded` | Too many requests | Example error: ```json theme={null} { "error": { "code": "invalid_request", "message": "Invalid status value", "param": "status" } } ``` ## Related Endpoints * [Create Product](/api-reference/endpoint/create-product-v2) - POST /api/v2/products * [Get Product](/api-reference/endpoint/get-product-v2) - GET /api/v2/products/ * [Update Product](/api-reference/endpoint/update-product-v2) - PUT /api/v2/products/ * [Check Eligibility](/api-reference/endpoint/check-eligibility) - POST /api/v2/products/check-eligibility ## Related Resources * [Product Object](/api-reference/objects/product) * [Webhooks Reference](/developer-manual/webhooks) # List Subscriptions Source: https://docs.withgale.com/api-reference/endpoint/list-subscriptions GET /api/v2/subscriptions Retrieve a list of all subscriptions with optional filtering # List Subscriptions Returns a paginated list of subscriptions, ordered by creation date (most recent first). ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Query Parameters | Parameter | Type | Description | | ---------------- | ------- | ------------------------------------------------------------------------ | | `limit` | integer | Number of results per page (default: 10, max: 100) | | `starting_after` | string | Cursor for pagination (subscription ID) | | `status` | enum | Filter by status: `trialing`, `active`, `past_due`, `cancelled`, `ended` | | `customer_id` | string | Filter by customer ID | ## Request ```bash theme={null} GET /api/v2/subscriptions?limit=10&status=active ``` ## Response ```json theme={null} { "data": [ { "id": "sub_abc123", "customer_id": "cus_xyz789", "status": "active", "amount_cents": 9900, "interval": "monthly", "next_billing_date": "2025-11-18T14:30:00Z", "created_at": "2025-10-18T14:30:00Z" } ], "has_more": true, "next_cursor": "sub_abc123" } ``` ## Examples ```bash theme={null} curl "https://api.withgale.com/api/v2/subscriptions?status=active" \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ## Related Endpoints * [Get Subscription](/api-reference/endpoint/get-subscription) * [Create Subscription](/api-reference/endpoint/create-subscription) # Update Product Source: https://docs.withgale.com/api-reference/endpoint/update-product-v2 PUT /api/v2/products/{id} Update an existing product's information # Update Product Update product details including price, description, images, and metadata. Changes to UPC code will trigger a new eligibility check. ## Authentication ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` ## Path Parameters | Parameter | Type | Required | Description | | --------- | ------- | -------- | ----------- | | `id` | integer | Yes | Product ID | ## Request Body ```json theme={null} { "name": "Premium Blood Pressure Monitor", "tagline": "Professional-grade automatic BP monitoring", "description": "Updated description with new features", "price": 5495, "images": [ "https://cdn.example.com/bp-monitor-updated.jpg" ], "metadata": { "category": "medical_devices", "brand": "HealthTech Pro", "version": "2.0" } } ``` ## Parameters All parameters are optional. Only include fields you want to update. | Parameter | Type | Description | | --------------------- | ------- | -------------------------------------------------- | | `name` | string | Product name (max 255 chars) | | `tagline` | string | Short description (max 255 chars) | | `description` | string | Full product description | | `price` | integer | Price in cents | | `currency` | string | ISO 4217 currency code | | `merchant_product_id` | string | Your reference ID | | `upc_code_or_gtin` | string | UPC or GTIN (triggers eligibility re-check) | | `status` | enum | `active`, `inactive`, or `archived` | | `images` | array | Array of image URLs (replaces all existing images) | | `metadata` | object | Custom key-value pairs (merged with existing) | All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ## Request ```bash theme={null} PUT /api/v2/products/{id} ``` ## Response Returns the updated product object: ```json theme={null} { "id": 12345, "name": "Premium Blood Pressure Monitor", "tagline": "Professional-grade automatic BP monitoring", "description": "Updated description with new features", "price": 5495, "currency": "USD", "merchant_product_id": "BP-MONITOR-001", "upc_code_or_gtin": "14567890123456", "status": "active", "eligibility": { "hsa_fsa_eligible": true, "message": "SIGIS verified - Medical device", "checked_at": "2025-10-18T14:30:00Z" }, "images": [ { "id": 569, "url": "https://cdn.example.com/bp-monitor-updated.jpg", "is_primary": true } ], "metadata": { "category": "medical_devices", "brand": "HealthTech Pro", "version": "2.0" }, "created_at": "2025-10-18T14:30:00Z", "updated_at": "2025-10-18T16:00:00Z" } ``` ## Examples ### Update Price ```bash theme={null} curl -X PUT https://api.withgale.com/api/v2/products/12345 \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "price": 5495 }' ``` ### Update Description and Status ```bash theme={null} curl -X PUT https://api.withgale.com/api/v2/products/12345 \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "description": "Enhanced model with improved accuracy and larger display", "tagline": "Professional-grade BP monitor", "status": "inactive" }' ``` ## Metadata Merging Metadata updates are merged with existing values, not replaced: ```javascript theme={null} // Existing metadata { "metadata": { "category": "medical", "brand": "HealthTech" } } // Update request { "metadata": { "inventory": 50 } } // Result (merged) { "metadata": { "category": "medical", "brand": "HealthTech", "inventory": 50 } } ``` To remove a metadata field, set it to `null`: ```json theme={null} { "metadata": { "old_field": null } } ``` ## Webhooks Product updates trigger the following webhook event: ```json theme={null} { "type": "product.updated", "data": { "id": 12345, "name": "Premium Blood Pressure Monitor", "price": 5495, "updated_at": "2025-10-18T16:00:00Z" } } ``` See [Webhooks Reference](/developer-manual/webhooks) for details. ## Errors | Status Code | Error Code | Description | | ----------- | --------------------- | -------------------------- | | 400 | `invalid_request` | Invalid parameters | | 401 | `unauthorized` | Invalid or missing API key | | 404 | `not_found` | Product not found | | 422 | `validation_error` | Field validation failed | | 429 | `rate_limit_exceeded` | Too many requests | Example error: ```json theme={null} { "error": { "code": "validation_error", "message": "Invalid price", "details": [ { "field": "price", "message": "Price must be a positive integer" } ] } } ``` ## Related Endpoints * [Create Product](/api-reference/endpoint/create-product-v2) - POST /api/v2/products * [Get Product](/api-reference/endpoint/get-product-v2) - GET /api/v2/products/ * [List Products](/api-reference/endpoint/list-products-v2) - GET /api/v2/products * [Check Eligibility](/api-reference/endpoint/check-eligibility) - POST /api/v2/products/check-eligibility ## Related Resources * [Product Object](/api-reference/objects/product) * [Webhooks Reference](/developer-manual/webhooks) # Introduction Source: https://docs.withgale.com/api-reference/introduction Complete reference for all Gale API endpoints - products, checkout, orders, payments, and subscriptions. # API Reference Complete documentation for Gale's REST API. Each endpoint includes request/response formats, authentication details, and working code examples. See [Authentication](/developer-manual/authentication) for API keys and setup. *** Create, list, update, and check HSA/FSA eligibility for products. Create checkout sessions for hosted payment pages. View orders, process refunds, and track payment status. Generate shareable payment URLs for off-platform payments. Create and manage recurring billing subscriptions. Receive real-time events for orders, payments, and subscriptions. # Checkout Session Object Source: https://docs.withgale.com/api-reference/objects/checkout-session A temporary checkout session that leads to an Order when paid # Checkout Session Object A checkout session represents a temporary payment intent created when you redirect customers to Gale's hosted checkout page. When the customer completes payment, the session converts to an **Order**. All monetary amounts are integers in cents (e.g., 4995 = \$49.95). **Checkout sessions are ephemeral** — they expire after 24 hours. The **Order** object is your permanent payment record. ## The Checkout Session Object ```json theme={null} { "checkout_id": "01HXYZ...", "checkout_url": "https://checkout.withgale.com/checkout/01HXYZ...", "reference_id": "your-order-123", "status": "open", "type": "eligible", "customer": { "email": "customer@example.com", "first_name": "Jane", "last_name": "Doe", "phone": "+1-555-123-4567" }, "line_items": [ { "product_id": "PROD-001", "name": "Digital Thermometer", "image_url": "https://yourcdn.com/thermometer.jpg", "price": 2995, "quantity": 1, "total": 2995, "currency": "USD", "hsa_fsa_eligible": true } ], "shipping_info": { "address_line_1": "123 Main St", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" }, "amounts": { "subtotal": 2995, "hsa_amount": 2995, "regular_amount": 0, "shipping": 995, "tax": 245, "discount": 0, "total": 4235 }, "success_url": "https://yoursite.com/order/success", "failure_url": "https://yoursite.com/order/failed", "metadata": { "platform": "custom" }, "order_id": null, "expires_at": "2026-02-26T14:30:00Z", "created_at": "2026-02-25T14:30:00Z" } ``` ## Attributes | Attribute | Type | Description | | --------------- | --------- | ------------------------------------------------- | | `checkout_id` | string | Unique checkout session identifier | | `checkout_url` | string | Hosted checkout page URL to redirect customer | | `reference_id` | string | Your order/cart ID for correlation | | `status` | enum | Session status (see below) | | `type` | enum | Checkout type: `eligible`, `split`, or `regular` | | `customer` | object | Customer information | | `line_items` | array | Products being purchased | | `shipping_info` | object | Shipping address | | `billing_info` | object | Billing address | | `amounts` | object | Price breakdown | | `success_url` | string | Redirect URL after successful payment | | `failure_url` | string | Redirect URL if payment fails or customer cancels | | `metadata` | object | Custom key-value pairs | | `order_id` | string | Created order ID (null until paid) | | `expires_at` | timestamp | When session expires (24 hours) | | `created_at` | timestamp | When session was created | ## Line Item Attributes | Attribute | Type | Description | | ------------------ | ------- | -------------------------------------- | | `product_id` | string | Your product ID | | `name` | string | Product name | | `image_url` | string | Product image URL | | `price` | integer | Unit price in cents | | `quantity` | integer | Quantity ordered | | `total` | integer | Line total in cents (price x quantity) | | `currency` | string | Currency code | | `hsa_fsa_eligible` | boolean | Whether this item is HSA/FSA eligible | ## Checkout Session Status | Status | Description | | --------- | ----------------------------------------- | | `open` | Session created, awaiting customer | | `paid` | Customer completed payment, order created | | `expired` | Session expired (24 hours passed) | ## Checkout Type Gale automatically determines the type based on product eligibility: | Type | Description | | ---------- | -------------------------------------- | | `eligible` | All items are HSA/FSA eligible | | `split` | Mix of eligible and non-eligible items | | `regular` | No items are eligible | ## Lifecycle ``` open → paid (payment succeeds, Order created) ↓ expired (24 hours, no payment) ``` When status becomes `paid`: * An **Order** is created * `order_id` field is populated * `order.created` webhook fires * Customer redirects to `success_url` ## Tracking Checkout Status ```bash theme={null} GET /api/v2/checkout/{checkout_id} ``` **Response when open:** ```json theme={null} { "checkout_id": "01HXYZ...", "status": "open", "order_id": null, "expires_at": "2026-02-26T14:30:00Z" } ``` **Response when paid:** ```json theme={null} { "checkout_id": "01HXYZ...", "status": "paid", "order_id": "ord_abc123", "paid_at": "2026-02-25T15:30:00Z" } ``` Once paid, query the **Order** for full payment details: ```bash theme={null} GET /api/v2/orders/{order_id} ``` ## Amounts Object ```json theme={null} { "amounts": { "subtotal": 2995, "hsa_amount": 2995, "regular_amount": 0, "shipping": 995, "tax": 245, "discount": 0, "total": 4235 } } ``` | Field | Description | | ---------------- | -------------------------------- | | `subtotal` | Sum of all line item totals | | `hsa_amount` | Amount payable with HSA/FSA | | `regular_amount` | Amount requiring regular payment | | `shipping` | Shipping cost | | `tax` | Tax amount | | `discount` | Discount applied | | `total` | Final total charged | Gale automatically calculates how much can be paid with HSA/FSA vs regular payment methods based on product eligibility. ## Subscription Sessions For subscription checkouts, include subscription details: ```json theme={null} { "payment_type": "subscription", "subscription": { "interval": "monthly", "interval_count": 1, "trial_period_days": 14 } } ``` When paid, creates both an **Order** and a **Subscription** object. Subscription checkout is currently in Beta. ## Webhooks Checkout sessions trigger these webhooks: * `order.created` — Payment succeeded, order created (this is what you should listen to) * `order.failed` — Payment failed **Note:** Listen to `order.created` to confirm payment. ## Best Practices **Don't poll for status** — Use webhooks instead: ```javascript theme={null} // Prefer: Webhooks app.post('/webhooks/gale', (req, res) => { if (req.body.type === 'order.created') { await fulfillOrder(req.body.data); } res.status(200).send('OK'); }); ``` **Sessions expire** — Don't store checkout URLs. Create new sessions for each purchase attempt. **Order is your source of truth** — Once paid, always reference the Order object, not the checkout session. ## Related Endpoints * [Create Checkout Session](/api-reference/endpoint/create-checkout-v2) — POST /api/v2/checkout * [Get Checkout Session](/api-reference/endpoint/get-checkout) — GET /api/v2/checkout/ * [List Orders](/api-reference/endpoint/list-orders) — GET /api/v2/orders ## Related Objects * [Order Object](/api-reference/objects/order) — Permanent payment record * [Product Object](/api-reference/objects/product) — Items being purchased * [Subscription Object](/api-reference/objects/subscription) — For recurring payments # Order Object Source: https://docs.withgale.com/api-reference/objects/order The Order object represents a completed or in-progress purchase created from a checkout session or payment link # Order Object When a payment is successfully processed — via checkout session or payment link — an Order is created. Orders track payment status, fulfillment, and provide an immutable record of the transaction. All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ## The Order Object ```json theme={null} { "id": "ord_01ABC...", "checkout_id": "cs_abc123", "reference_id": "your-order-123", "payment_link_id": null, "client_reference_id": null, "status": "completed", "is_recurring": false, "payment_status": "captured", "customer": { "email": "jane@example.com", "first_name": "Jane", "last_name": "Doe", "phone": "+1-555-123-4567", "shipping_address": { "address_line_1": "123 Main St", "address_line_2": "Apt 4B", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" }, "billing_address": { "address_line_1": "123 Main St", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" } }, "line_items": [ { "id": "li_123", "product_id": "PROD-001", "name": "Digital Thermometer", "quantity": 1, "price": 2995, "hsa_fsa_eligible": true, "is_recurring": false } ], "amounts": { "subtotal": 2995, "hsa_amount": 2995, "regular_amount": 0, "shipping": 995, "tax": 245, "discount": 0, "total": 4235 }, "payment": { "payment_id": "pay_xyz789", "payment_method": "hsa_fsa_card", "last4": "1111", "brand": "visa", "captured_at": "2026-02-25T15:30:00Z" }, "metadata": { "order_number": "ORDER-12345", "platform": "CUSTOM" }, "created_at": "2026-02-25T15:00:00Z", "completed_at": "2026-02-25T15:30:00Z", "status_history": [ { "status": "pending", "timestamp": "2026-02-25T15:00:00Z", "reason": "Order created" }, { "status": "processing", "timestamp": "2026-02-25T15:01:00Z", "reason": "Payment processing" }, { "status": "completed", "timestamp": "2026-02-25T15:30:00Z", "reason": "Payment captured" } ] } ``` ## Attributes | Attribute | Type | Description | | --------------------- | --------- | ------------------------------------------------------------------------------------------------------ | | `id` | string | Unique order identifier (`ord_xxx`) | | `checkout_id` | string | Checkout session ID (`cs_xxx`) | | `reference_id` | string | Your order/cart reference — set for checkout session orders, null for payment link orders | | `payment_link_id` | string | Payment link ID — set for payment link orders, null for checkout session orders | | `client_reference_id` | string | Your reference passed via payment link — set for payment link orders, null for checkout session orders | | `status` | enum | Fulfillment status (see below) | | `is_recurring` | boolean | `true` if this is a subscription/recurring order, `false` for one-time purchases | | `payment_status` | enum | Payment state (see below) | | `amount` | integer | Order total in cents | | `currency` | string | Lowercase ISO currency code (e.g. `usd`) | | `customer` | object | Customer name and email | | `line_items` | array | Ordered items (snapshot at time of purchase) | | `created_at` | timestamp | When order was created | ## Amounts Object | Field | Type | Description | | ---------------- | ------- | -------------------------------- | | `subtotal` | integer | Sum of all line item totals | | `hsa_amount` | integer | Amount payable with HSA/FSA | | `regular_amount` | integer | Amount requiring regular payment | | `shipping` | integer | Shipping cost | | `tax` | integer | Tax amount | | `discount` | integer | Discount applied | | `total` | integer | Final total charged | ## Line Item Attributes | Attribute | Type | Description | | ------------------ | ------- | ------------------------------------------------------- | | `id` | string | Line item identifier | | `product_id` | string | Your product ID | | `name` | string | Product name | | `quantity` | integer | Quantity ordered | | `price` | integer | Unit price in cents | | `hsa_fsa_eligible` | boolean | Whether this item is HSA/FSA eligible | | `is_recurring` | boolean | Whether this line item is a recurring/subscription item | ## Order Status | Status | Description | | ------------ | ---------------------------------- | | `pending` | Order created, awaiting payment | | `processing` | Payment being processed | | `completed` | Payment successful, order complete | | `failed` | Payment failed | | `cancelled` | Order cancelled | ## Payment Status | Status | Description | | ------------ | ---------------------------------- | | `pending` | Payment not yet attempted | | `authorized` | Payment authorized (not captured) | | `captured` | Payment captured successfully | | `failed` | Payment failed | | `refunded` | Payment refunded (full or partial) | | `disputed` | Payment disputed/chargebacked | ## Order Lifecycle An order moves through these states: * **pending** - Order created, awaiting payment * **processing** - Payment initiated * **completed** - Payment captured successfully * **failed** - Payment declined or failed * **cancelled** - Order cancelled by customer or merchant * **refunded** - Completed order was refunded (full or partial) * **disputed** - Completed order has a chargeback filed ## Status History Track all status changes: ```json theme={null} { "status_history": [ { "status": "pending", "timestamp": "2026-02-25T15:00:00Z", "reason": "Order created" }, { "status": "processing", "timestamp": "2026-02-25T15:01:00Z", "reason": "Customer submitted payment" }, { "status": "completed", "timestamp": "2026-02-25T15:30:00Z", "reason": "Payment captured successfully" } ] } ``` ## Related Endpoints * [Get Order](/api-reference/endpoint/get-order) - GET /api/v2/orders/ * [List Orders](/api-reference/endpoint/list-orders) - GET /api/v2/orders * [Refund Order](/api-reference/endpoint/create-refund) - POST /api/v2/refunds # Product Object Source: https://docs.withgale.com/api-reference/objects/product The Product object represents an item you sell, with automatic HSA/FSA eligibility detection # Product Object The Product object represents items in your catalog. Gale automatically determines HSA/FSA eligibility via SIGIS database integration. All monetary amounts are integers in cents (e.g., 4995 = \$49.95). ## The Product Object ```json theme={null} { "id": 12345, "name": "Digital Blood Pressure Monitor", "tagline": "FDA-approved automatic BP monitor", "description": "Clinically validated blood pressure monitor with Bluetooth connectivity. Features automatic inflation, irregular heartbeat detection, and memory for 200 readings. Includes carrying case and 4 AAA batteries.", "price": 4995, "currency": "USD", "merchant_product_id": "BP-MONITOR-001", "upc_code_or_gtin": "14567890123456", "status": "active", "eligibility": { "hsa_fsa_eligible": true, "message": "HSA/FSA accepted" }, "images": [ { "id": 567, "url": "https://cdn.example.com/bp-monitor-front.jpg", "is_primary": true }, { "id": 568, "url": "https://cdn.example.com/bp-monitor-side.jpg", "is_primary": false } ], "metadata": { "category": "medical_devices", "brand": "HealthTech Pro", "weight": "0.5 lbs" }, "created_at": "2026-02-25T14:30:00Z", "updated_at": "2026-02-25T14:30:00Z" } ``` ## Attributes | Attribute | Type | Description | | --------------------- | --------- | ------------------------------------------------ | | `id` | integer | Unique identifier for the product | | `name` | string | Product name (max 255 chars) | | `tagline` | string | Short description (max 255 chars) | | `description` | string | Full product description (supports markdown) | | `price` | integer | Price in cents | | `currency` | string | ISO 4217 currency code | | `merchant_product_id` | string | Your reference ID for this product | | `upc_code_or_gtin` | string | UPC or GTIN barcode | | `status` | enum | Product status: `active`, `inactive`, `archived` | | `eligibility` | object | HSA/FSA eligibility information | | `images` | array | Product images (max 10) | | `metadata` | object | Custom key-value pairs | | `created_at` | timestamp | When product was created | | `updated_at` | timestamp | When product was last updated | ## Eligibility Object | Field | Type | Description | | ------------------ | ------- | --------------------------------------- | | `hsa_fsa_eligible` | boolean | Whether product is HSA/FSA eligible | | `message` | string | Customer-facing eligibility explanation | Gale determines eligibility automatically when you sync products. The `message` field provides a customer-facing explanation you can display on your storefront (e.g., "HSA/FSA accepted", "Requires Letter of Medical Necessity"). ## Product Status | Status | Description | | ---------- | ----------------------------------- | | `active` | Available for sale | | `inactive` | Temporarily unavailable | | `archived` | Removed from catalog (soft deleted) | ## Examples ### Basic Product (Required Fields Only) ```json theme={null} { "name": "Digital Thermometer", "price": 2995, "currency": "USD" } ``` ### Complete Product ```json theme={null} { "name": "Premium Blood Pressure Monitor", "tagline": "Professional-grade BP monitoring", "description": "Clinically validated automatic blood pressure monitor with advanced features including irregular heartbeat detection, WHO classification indicator, and Bluetooth connectivity for health app integration.", "price": 4995, "currency": "USD", "merchant_product_id": "BP-MONITOR-PRO", "upc_code_or_gtin": "14567890123456", "images": [ "https://cdn.example.com/bp-monitor-1.jpg", "https://cdn.example.com/bp-monitor-2.jpg" ], "metadata": { "category": "medical_devices", "brand": "HealthTech", "features": "bluetooth,memory,irregular_detection" } } ``` ## Related Endpoints * [Create Product](/api-reference/endpoint/create-product-v2) - POST /api/v2/products * [Get Product](/api-reference/endpoint/get-product-v2) - GET /api/v2/products/ * [Update Product](/api-reference/endpoint/update-product-v2) - PUT /api/v2/products/ * [List Products](/api-reference/endpoint/list-products-v2) - GET /api/v2/products * [Check Eligibility](/api-reference/endpoint/check-eligibility) - POST /api/v2/products/check-eligibility # Subscription Object Source: https://docs.withgale.com/api-reference/objects/subscription The Subscription object represents a recurring payment schedule for a product or service # Subscription Object Subscriptions enable recurring payments for memberships, services, or product deliveries. They automatically charge customers at specified intervals using their stored HSA/FSA or regular payment method. ## The Subscription Object ```json theme={null} { "id": "sub_abc123xyz", "customer_id": "cus_xyz789", "status": "active", "product_id": 12345, "merchant_product_id": "MEMBERSHIP-PREMIUM", "amount_cents": 9900, "currency": "USD", "interval": "monthly", "interval_count": 1, "trial_period_days": 7, "trial_end": "2025-10-25T14:30:00Z", "current_period_start": "2025-10-18T14:30:00Z", "current_period_end": "2025-11-18T14:30:00Z", "next_billing_date": "2025-11-18T14:30:00Z", "cancel_at_period_end": false, "cancelled_at": null, "ended_at": null, "payment_method": { "type": "hsa_fsa_card", "last4": "1111", "brand": "visa" }, "metadata": { "tier": "premium", "features": "unlimited_access,priority_support" }, "created_at": "2025-10-18T14:30:00Z", "updated_at": "2025-10-18T14:30:00Z" } ``` ## Attributes | Attribute | Type | Description | | ---------------------- | --------- | -------------------------------------------------------- | | `id` | string | Unique subscription identifier | | `customer_id` | string | Customer ID | | `status` | enum | Subscription status (see below) | | `product_id` | integer | Associated product ID | | `merchant_product_id` | string | Your product reference ID | | `amount_cents` | integer | Recurring charge amount in cents | | `currency` | string | ISO 4217 currency code | | `interval` | enum | Billing interval: `daily`, `weekly`, `monthly`, `yearly` | | `interval_count` | integer | Number of intervals between billings | | `trial_period_days` | integer | Free trial duration in days | | `trial_end` | timestamp | When trial ends (null if no trial) | | `current_period_start` | timestamp | Start of current billing period | | `current_period_end` | timestamp | End of current billing period | | `next_billing_date` | timestamp | Next scheduled charge date | | `cancel_at_period_end` | boolean | Whether subscription cancels at period end | | `cancelled_at` | timestamp | When subscription was cancelled (null if active) | | `ended_at` | timestamp | When subscription ended (null if active) | | `payment_method` | object | Stored payment method details | | `metadata` | object | Custom key-value pairs | | `created_at` | timestamp | When subscription was created | | `updated_at` | timestamp | When subscription was last updated | ## Subscription Status | Status | Description | | ----------- | --------------------------------- | | `trialing` | In free trial period | | `active` | Active and billing normally | | `past_due` | Payment failed, retrying | | `cancelled` | Cancelled, will end at period end | | `ended` | Subscription has ended | | `paused` | Temporarily paused | ## Billing Intervals | Interval | `interval_count` | Billing Frequency | | --------- | ---------------- | -------------------------- | | `daily` | 1 | Every day | | `daily` | 7 | Every 7 days | | `weekly` | 1 | Every week | | `weekly` | 2 | Every 2 weeks (bi-weekly) | | `monthly` | 1 | Every month | | `monthly` | 3 | Every 3 months (quarterly) | | `yearly` | 1 | Every year | ## Subscription Lifecycle A subscription moves through these states: * **trialing** - Subscription created with trial period * **active** - Subscription is active and billing normally (can come from trialing or be created without trial) * **past\_due** - Payment failed, retrying (can come from trialing or active) * **cancelled** - Cancel requested, will end at period end * **ended** - Subscription has ended (can come from cancelled, past\_due after max retries, or active for immediate cancellation) ## Trial Periods Subscriptions can include a free trial period: ```json theme={null} { "trial_period_days": 7, "trial_end": "2025-10-25T14:30:00Z", "status": "trialing", "next_billing_date": "2025-10-25T14:30:00Z" } ``` During trial: * Customer is not charged * Payment method is verified * First charge occurs when trial ends ## Payment Method ```json theme={null} { "payment_method": { "type": "hsa_fsa_card", "last4": "1111", "brand": "visa", "exp_month": 12, "exp_year": 2026 } } ``` | Type | Description | | -------------- | ------------------- | | `hsa_fsa_card` | HSA/FSA debit card | | `credit_card` | Regular credit card | | `debit_card` | Regular debit card | ## Cancellation Behavior ### Cancel at Period End ```json theme={null} { "status": "cancelled", "cancel_at_period_end": true, "current_period_end": "2025-11-18T14:30:00Z", "cancelled_at": "2025-10-20T10:00:00Z", "ended_at": null } ``` Customer retains access until period end. ### Immediate Cancellation ```json theme={null} { "status": "ended", "cancel_at_period_end": false, "cancelled_at": "2025-10-20T10:00:00Z", "ended_at": "2025-10-20T10:00:00Z" } ``` Access ends immediately. ## Past Due Handling When a payment fails: 1. Status changes to `past_due` 2. Automatic retry attempts (days 3, 7, 14) 3. Customer receives notification 4. After max retries, subscription ends ```json theme={null} { "status": "past_due", "next_billing_date": "2025-10-21T14:30:00Z", "retry_count": 2, "max_retries": 3 } ``` ## Examples ### Monthly Membership ```json theme={null} { "id": "sub_membership_001", "amount_cents": 9900, "interval": "monthly", "interval_count": 1, "status": "active", "trial_period_days": 14 } ``` ### Quarterly Subscription ```json theme={null} { "id": "sub_quarterly_001", "amount_cents": 24900, "interval": "monthly", "interval_count": 3, "status": "active" } ``` ### Annual Plan with Trial ```json theme={null} { "id": "sub_annual_001", "amount_cents": 99900, "interval": "yearly", "interval_count": 1, "status": "trialing", "trial_period_days": 30, "trial_end": "2025-11-18T14:30:00Z" } ``` ## Related Endpoints * [Create Subscription](/api-reference/endpoint/create-subscription) - POST /api/v2/subscriptions * [Get Subscription](/api-reference/endpoint/get-subscription) - GET /api/v2/subscriptions/ * [List Subscriptions](/api-reference/endpoint/list-subscriptions) - GET /api/v2/subscriptions * [Cancel Subscription](/api-reference/endpoint/cancel-subscription) - POST /api/v2/subscriptions//cancel * [Update Subscription](/api-reference/endpoint/update-subscription) - PUT /api/v2/subscriptions/ # Monthly Product Updates Source: https://docs.withgale.com/changelog/updates ## Products now available in your dashboard Merchant-Dashboard-product-sync You can now view and edit products in the Gale Dashboard. The new Products section allows merchants to: * Import products in bulk via CSV * View eligibility details and update information * Sync products from connected stores ## Shopify HSA/FSA post-purchase reimbursements Shopify post-purchase reimbursements We’ve launched a new way for Shopify merchants to boost cart sizes and repeat purchases. Shoppers can now pay with any card and still enjoy automatic HSA/FSA reimbursements — making it easier than ever to save using pre-tax dollars. ## Merchant Dashboard is live Merchant-Dashboard-Live Merchants no longer have had to take action on their account and orders via their eCommerce platform. Our Merchant dashboard includes, log in, sign up, secure authentication, and account management. ## Generate API Keys Self-serve-API-Keys Merchants can now generate and manage their own API keys directly from the dashboard, eliminating support tickets for key generation and enabling faster testing workflows. ## Letter of Medical Necessity – Launched LMN-Launched Our Letter of Medical Necessity (LMN) product is now live and out of Beta. Merchants that sell health and wellness products or services that are not on the eligible product list can now offer HSA | FSA eligibility to shoppers that qualify. ## Integration Enhancements * Multi-currency support added to WooCommerce plugin * Background product catalog synchronization – no more waiting for your products to sync ## Refunds available in Shopify Shopify-refunds The Gale Shopify App now allows you to issue full refunds for Gale orders directly from your Shopify dashboard — making it quicker and easier to action returns. ## Our API Docs are now public Woocommerce Product Sync Our custom integration guides are now out of beta and available for all merchants. [View API Docs](/api-reference/introduction) ## HSA & FSA payments for optical stores Woocommerce Product Sync Gale’s payment platform now supports brands selling vision and eyewear products to consumers. Our platform includes item-level validation, reducing card rejection and protecting merchants and shoppers against compliance risk. ## Hosted Payment Page redesign Woocommerce Product Sync This month we shipped a redesign of our Hosted Payment Page. The new design features a simple, modern, and beautiful UI. After seeing positive conversion rate improvements, we’ve rolled this out to all merchants across all platforms. ## Ecwid integration now available (in beta) Woocommerce Product Sync Our Ecwid plugin is now here! Merchants on the Ecwid by Lightspeed eCommerce platform can now add HSA & FSA payments in minutes — no-code required.\ Interested? Reach out to your Account Manager or [Gale Support](https://www.withgale.com/contact/support) for early access. ## SIGIS Certified Integrated Payment provider Sigis Gale Gale is now a Certified SIGIS Integrated Payment Provider. The certification enables us to provide an IIAS system and accept FSA/HSA cards on behalf of eCommerce merchants. ## On-site Messaging 2.0 Woocommerce Product Sync We launched four beautiful new variants of our on-site messaging (OSM) for merchants across all integrated platforms. Promote HSA/FSA eligibility and improve add-to-cart rates. ## Test Mode for Hosted Payment Page We’ve added a “Test Mode” toggle in all of our plugins and integrations — allowing developers to build and test payments without triggering real transactions. ## WooCommerce Plugin now live Woocommerce Product Sync Our WooCommerce plugin has been approved by the WordPress team and is now available on the WordPress Marketplace.\ [View Plugin](https://wordpress.org/plugins/gale-hsa-fsa-payments/) ## Shopify Payments App now available (in beta) Shopify Payments App Our Shopify Payments App has been tested and approved by the Shopify Partnership Team. It enables Shopify merchants to seamlessly integrate payments while ensuring compliance with HSA/FSA standards.\ [View App](https://apps.shopify.com/gale-hsa-fsa-payments) ## Shopify Product Eligibility App now live Shopify Eligibility App Merchants can now seamlessly display product eligibility and onsite messaging, helping customers easily identify which products qualify.\ [View App](https://apps.shopify.com/gale-hsa-fsa-eligibility) ## WooCommerce plugin improvements Woocommerce Product Sync Our WooCommerce Zip File plugin can now automatically sync your product catalog and determine eligibility at the click of a button — no more CSV file uploads. Check out the docs to get started. # Authentication Source: https://docs.withgale.com/developer-manual/authentication How to authenticate API requests and ensure safe retries using idempotency keys. # Authentication All requests to the Gale API must be authenticated using a valid API key. ## Base URL ``` https://api.withgale.com/api/v2 ``` ## API Keys ### Getting Your API Key 1. Log in to [Gale Dashboard](https://dashboard.withgale.com) 2. Navigate to **Settings** → **API Keys** 3. Copy your API key You'll see two types of keys: * **Test keys** (prefix: `glm_test_`) - For development and testing * **Live keys** (prefix: `glm_live_`) - For production ### Using Your API Key Include your API key in the `Authorization` header of every request: ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` **Example request:** ```bash theme={null} curl https://api.withgale.com/api/v2/products \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ### Authentication Errors Missing or invalid API keys return a `401 Unauthorized` response: ```json theme={null} { "error": { "code": "unauthorized", "message": "Invalid or missing API key" } } ``` ## Idempotency Prevent duplicate operations by using idempotency keys for state-changing requests. ### How It Works Add an `Idempotency-Key` header to prevent duplicate processing: ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/checkout \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \ -H "Content-Type: application/json" \ -d '{...}' ``` * **Key format**: Use a unique UUID (v4 recommended) * **Validity**: Keys are stored for 24 hours * **Behavior**: Requests with the same key within 24 hours return the original response without reprocessing ### When to Use Use idempotency keys for: * Creating checkout sessions (`POST /api/v2/checkout`) * Creating refunds (`POST /api/v2/refunds`) * Creating payment links (`POST /api/v2/payment-links`) * Any POST request that creates or modifies data ### Best Practices * Generate a new UUID for each operation * Retry failed requests with the **same** idempotency key * Store the key with your request for retry scenarios * Don't reuse keys across different operations **Example:** ```javascript theme={null} const { v4: uuidv4 } = require('uuid'); const createCheckout = async (data) => { const idempotencyKey = uuidv4(); const response = await fetch('https://api.withgale.com/api/v2/checkout', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Idempotency-Key': idempotencyKey, 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return response.json(); }; ``` ## Security Best Practices * **Never expose API keys** in client-side code or public repositories * **Use environment variables** to store API keys * **Rotate keys regularly** in production * **Use test keys** for development and staging environments * **Monitor API key usage** in your dashboard ## Related * [Rate Limits](/developer-manual/rate-limits) - API request limits * [Errors](/developer-manual/errors) - Error handling # Errors Source: https://docs.withgale.com/developer-manual/errors Learn about standardized error responses and how to interpret them. # Error Handling Gale returns consistent and descriptive error responses across all endpoints to help you debug and recover gracefully. ## Standard Error Format All errors follow this format: ```json theme={null} { "error": { "code": "validation_error", "message": "Validation failed: merchant_product_id is required.", "details": { "field_errors": { "merchant_product_id": ["This field is required"] } } } } ``` ## HTTP Status Codes | Status Code | Error Code | Meaning | When It Happens | | ----------- | --------------------- | ----------------- | ----------------------------------------- | | 400 | `bad_request` | Bad Request | Malformed JSON or missing required fields | | 401 | `unauthorized` | Unauthorized | Missing or invalid API key | | 403 | `forbidden` | Forbidden | Valid token but insufficient permissions | | 404 | `not_found` | Not Found | Resource does not exist | | 409 | `conflict` | Conflict | Invalid state change | | 422 | `validation_error` | Validation Failed | Field validation failed | | 429 | `rate_limit_exceeded` | Too Many Requests | Rate limit exceeded | | 500 | `internal_error` | Server Error | Something went wrong on Gale's side | ## Error Examples ### Validation Error (422) ```json theme={null} { "error": { "code": "validation_error", "message": "Validation failed", "details": { "field_errors": { "line_items": ["At least one line item is required"], "customer.email": ["Invalid email format"] } } } } ``` ### Unauthorized (401) ```json theme={null} { "error": { "code": "unauthorized", "message": "Invalid or missing API key" } } ``` ### Not Found (404) ```json theme={null} { "error": { "code": "not_found", "message": "Order not found" } } ``` ### Rate Limited (429) ```json theme={null} { "error": { "code": "rate_limit_exceeded", "message": "Too many requests. Please try again in 30 seconds." } } ``` Response includes `Retry-After` header indicating seconds to wait. ## Best Practices * **Check status code first**: Use HTTP status codes to determine error type * **Parse error details**: Check `error.details.field_errors` for specific validation failures * **Implement retry logic**: For 429 and 500 errors, retry with exponential backoff * **Log errors**: Store error responses for debugging ## Related * [Authentication](/developer-manual/authentication) - API key setup * [Rate Limits](/developer-manual/rate-limits) - Handling rate limits # Getting Started Source: https://docs.withgale.com/developer-manual/getting-started Get up and running with Gale's API in minutes # Getting Started This guide walks you through creating your first checkout session using Gale's API. By the end, you'll understand the full payment flow. ## Base URL ``` https://api.withgale.com/api/v2 ``` Use test API keys (prefix `glm_test_`) during development. All test transactions are processed in sandbox mode with no real charges. ## Authentication Include your API key in the `Authorization` header: ```bash theme={null} Authorization: Bearer glm_test_YOUR_API_KEY ``` Get your API keys from [Gale Dashboard](https://dashboard.withgale.com) under **Settings** > **API Keys**. ## Quick Start ### 1. Sync a Product Before creating a checkout, sync your product catalog so Gale can determine HSA/FSA eligibility. ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/products \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Digital Blood Pressure Monitor", "merchant_product_id": "BP-MONITOR-001", "upc_code_or_gtin": "14567890123456", "price": 4995, "currency": "USD" }' ``` The response includes eligibility status: ```json theme={null} { "success": true, "data": { "id": 12345, "name": "Digital Blood Pressure Monitor", "merchant_product_id": "BP-MONITOR-001", "price": 4995, "eligibility": { "hsa_fsa_eligible": true, "message": "SIGIS verified - Medical device" } } } ``` ### 2. Create a Checkout Session Create a checkout session and get a URL to redirect your customer to: ```bash theme={null} curl -X POST https://api.withgale.com/api/v2/checkout \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "reference_id": "your-order-123", "customer": { "email": "jane@example.com", "first_name": "Jane", "last_name": "Doe" }, "line_items": [ { "product_id": "BP-MONITOR-001", "name": "Digital Blood Pressure Monitor", "price": 4995, "quantity": 1 } ], "shipping_info": { "address_line_1": "123 Main St", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" }, "shipping": 500, "tax": 410, "success_url": "https://yoursite.com/order/success", "failure_url": "https://yoursite.com/order/failed" }' ``` Response: ```json theme={null} { "success": true, "checkout_id": "01HXYZ...", "checkout_url": "https://checkout.withgale.com/checkout/01HXYZ...", "status": "open", "expires_at": "2026-02-26T14:30:00Z" } ``` ### 3. Redirect Customer Send your customer to the `checkout_url`. Gale's hosted checkout page handles: * HSA/FSA card collection * Eligibility verification * Letter of Medical Necessity (LMN) flow for dual-purpose items * Payment processing After payment, the customer is redirected to your `success_url` or `failure_url`. ### 4. Confirm Payment **Option A: Webhooks (recommended)** Register a webhook endpoint in your [Dashboard](https://dashboard.withgale.com) under **Settings** > **Webhooks**. Gale sends an `order.created` event when payment succeeds: ```json theme={null} { "type": "order.created", "data": { "id": "ord_abc123", "status": "completed", "checkout_id": "01HXYZ...", "reference_id": "your-order-123", "customer": { "email": "jane@example.com" } } } ``` **Option B: Poll for status** Check the checkout status by querying the order: ```bash theme={null} curl https://api.withgale.com/api/v2/orders/{order_id} \ -H "Authorization: Bearer glm_test_YOUR_API_KEY" ``` ## Three Key IDs | ID | What it is | When you get it | | -------------- | ------------------------------------------- | ----------------------------------------- | | `checkout_id` | The checkout session you created | Returned when you create a checkout | | `order_id` | The payment record after successful payment | Returned in webhooks and order queries | | `reference_id` | Your own order/cart ID for correlation | You provide this when creating a checkout | ## Testing Use test API keys (`glm_test_*`) during development. Test card numbers: | Card Number | Result | | --------------------- | ------------------ | | `4111 1111 1111 1111` | Successful payment | | `4000 0000 0000 0002` | Card declined | See [Test Cards](/resources/test-cards) for the full list. ## Next Steps Full end-to-end integration walkthrough Detailed checkout integration guide Complete API endpoint documentation Set up real-time event notifications # Integration Flow Source: https://docs.withgale.com/developer-manual/integration-flow Understand the end-to-end flow of integrating with the Gale API platform. This page outlines the complete sequence of steps required to successfully integrate with Gale's API platform. ## 1. Product Sync Before initiating any checkout session, sync your product catalog with Gale. Each product includes metadata such as name, UPC/GTIN, and a unique product ID from your system. Endpoint: ``` POST /api/v2/products ``` This step ensures Gale has full context of the items you intend to sell through the hosted checkout. See the [Product API Reference](/api-reference/endpoint/create-product-v2) for full details. ## 2. Eligibility Classification Once products are synced, Gale automatically checks HSA/FSA eligibility against the SIGIS database. Each product response includes: * `hsa_fsa_eligible` — boolean indicating whether the product qualifies for HSA/FSA * `message` — a customer-facing explanation you can display on your storefront (e.g., "HSA/FSA accepted", "Requires Letter of Medical Necessity") You can: * Check the `hsa_fsa_eligible` field on any product response * Query eligibility using the [Check Eligibility](/api-reference/endpoint/check-eligibility) endpoint * Subscribe to webhook events (`product.updated`) for real-time eligibility updates ## 3. Hosted Checkout Session With eligible products in place, initiate a hosted checkout session using the Gale API. Endpoint: ``` POST /api/v2/checkout ``` You must include customer details, line items, shipping, tax, and a `reference_id` (your order or cart ID for correlation). A successful response returns a `checkout_id` and `checkout_url` which you redirect your customer to. Gale automatically determines the checkout type based on product eligibility: * **Eligible** - All items are HSA/FSA eligible, entire amount charged to HSA/FSA card * **Split** - Mix of eligible and non-eligible items, Gale handles splitting payment across HSA/FSA and regular payment methods * **Regular** - No items are eligible, standard payment processing See [Checkout API Reference](/api-reference/endpoint/create-checkout-v2) for full request/response details. ## 4. Payment Processing The customer completes payment on Gale's hosted checkout page. Once paid, an **order** is created. ### Order Lifecycle Orders track both fulfillment and payment separately: **Order Status** (your fulfillment state): * `pending` - Order created, awaiting payment * `processing` - Payment in progress * `completed` - Payment successful, ready to fulfill * `failed` - Payment declined * `cancelled` - Order cancelled **Payment Status** (money movement): * `pending` - Payment not attempted * `authorized` - Funds reserved (not yet captured) * `captured` - Funds captured successfully * `refunded` - Funds returned to customer This separation gives you flexibility. For example, an order can be `completed` while payment is still `authorized` (not yet captured). **Most merchants only need to check:** Order `status === 'completed'` means the order is paid and ready to ship. Check order status anytime: ``` GET /api/v2/orders/{order_id} ``` ### Three Key IDs Keep these distinct: | ID | What it is | When you get it | | -------------- | ------------------------------------------- | ----------------------------------------- | | `checkout_id` | The checkout session you created | Returned when you create a checkout | | `order_id` | The payment record after successful payment | Returned in webhooks and order queries | | `reference_id` | Your own order/cart ID for correlation | You provide this when creating a checkout | ## 5. Webhook Notifications Gale sends webhooks when important events occur. All payment flows (checkout sessions, payment links) create orders, so you'll receive order webhooks. ### Common Webhooks **For Checkout Sessions:** * `order.created` - Order created after successful payment * `order.failed` - Payment failed * `order.refunded` - Order was refunded **For Payment Links:** * `payment_link.paid` - Customer paid the link * `order.created` - Order created from the payment (fires after `payment_link.paid`) * `payment_link.expired` - Link expired without payment **For Products:** * `product.updated` - Eligibility status changed (e.g., SIGIS update) ### Setup 1. Register your webhook URL in **Settings** > **Webhooks** in the Gale Dashboard 2. Validate signatures using `X-Gale-Signature` header 3. Respond with HTTP 200 within 5 seconds 4. Gale retries failed deliveries with exponential backoff See [Webhooks Reference](/developer-manual/webhooks) for all event types and examples. ## 6. Refunds You can issue full refunds via the API: ``` POST /api/v2/refunds ``` For split checkout sessions (where payment was split across HSA/FSA and regular payment methods), partial refunds are managed through the [Gale Dashboard](https://dashboard.withgale.com). See [Refund API Reference](/api-reference/endpoint/create-refund) for details. ## Summary | Step | Description | | ------------------ | ----------------------------------------- | | Product Sync | Send your products to Gale | | Eligibility | Check which items are HSA/FSA eligible | | Create Checkout | Launch a hosted checkout session | | Process Payment | Customer completes payment on Gale's page | | Listen to Webhooks | Receive real-time updates | | Handle Refunds | Process returns via API or Dashboard | Refer to the API Reference section for implementation details of each step. # Rate Limits Source: https://docs.withgale.com/developer-manual/rate-limits Understand how Gale enforces rate limits and how to handle throttled requests. # Rate Limits Gale enforces rate limits to ensure consistent performance and prevent abuse. ## Current Limits | API Key Type | Requests per Minute | | ------------------------ | ------------------- | | Test keys (`glm_test_*`) | 100 | | Live keys (`glm_live_*`) | 1000 | Limits are enforced per API key across all endpoints. All requests count toward your limit regardless of HTTP method (GET, POST, etc.). ## Rate Limit Headers Every API response includes rate limit information: ``` X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 995 X-RateLimit-Reset: 1698345600 ``` * `X-RateLimit-Limit`: Total requests allowed per minute * `X-RateLimit-Remaining`: Requests remaining in current window * `X-RateLimit-Reset`: Unix timestamp when the limit resets ## Handling Rate Limits When you exceed the rate limit, you'll receive a `429` response: ```json theme={null} { "error": { "code": "rate_limit_exceeded", "message": "Too many requests. Please try again in 30 seconds." } } ``` Response includes: ``` HTTP/1.1 429 Too Many Requests Retry-After: 30 ``` The `Retry-After` header indicates seconds to wait before retrying. ## Best Practices ### 1. Implement Exponential Backoff ```javascript theme={null} async function makeRequestWithRetry(url, options, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { const response = await fetch(url, options); if (response.status === 429) { const retryAfter = response.headers.get('Retry-After') || (2 ** i); await new Promise(resolve => setTimeout(resolve, retryAfter * 1000)); continue; } return response; } throw new Error('Max retries exceeded'); } ``` ### 2. Use Webhooks Instead of Polling Don't poll for order status - use webhooks: **Bad:** ```javascript theme={null} // Polling every second setInterval(async () => { const order = await getOrder(orderId); }, 1000); ``` **Good:** ```javascript theme={null} // Use webhooks app.post('/webhooks/gale', (req, res) => { if (req.body.type === 'order.created') { handleOrder(req.body.data); } }); ``` ### 3. Batch Operations Use batch endpoints when available: ```javascript theme={null} // Instead of multiple requests for (const product of products) { await checkEligibility(product); // 100 requests } // Use batch endpoint await batchCheckEligibility(products); // 1 request ``` ### 4. Cache Results Cache eligibility checks and product data: ```javascript theme={null} const cache = new Map(); async function getProductEligibility(productId) { if (cache.has(productId)) { return cache.get(productId); } const result = await checkEligibility(productId); cache.set(productId, result); return result; } ``` ## Request Higher Limits Need higher limits for your production use case? Contact [support@withgale.com](mailto:support@withgale.com) with: * Your use case description * Expected request volume * Current API key ## Related * [Authentication](/developer-manual/authentication) - API key setup * [Errors](/developer-manual/errors) - Error handling # Webhooks Source: https://docs.withgale.com/developer-manual/webhooks Receive real-time notifications about events in your Gale account # Webhooks Webhooks allow your application to receive real-time notifications when events happen in your Gale account, such as completed payments, failed charges, and refunds. ## How Webhooks Work 1. **Customer** completes payment on Gale's checkout page 2. **Gale** processes the payment 3. **Gale sends** a POST request to your webhook endpoint with event data 4. **Your server** processes the event (e.g., fulfills order, sends email) 5. **Your server** returns a `200 OK` response 6. **Event is marked** as delivered successfully If your server doesn't respond with `200`, Gale retries the delivery automatically. ## Setup ### 1. Create an Endpoint on Your Server ```javascript theme={null} app.post('/webhooks/gale', async (req, res) => { // Acknowledge immediately — process async res.status(200).send('OK'); const event = req.body; switch (event.type) { case 'order.completed': await fulfillOrder(event.data); break; case 'order.failed': await notifyOrderFailed(event.data); break; case 'refund.succeeded': await handleRefund(event.data); break; } }); ``` ### 2. Register It in the Dashboard 1. Log in to [Gale Dashboard](https://dashboard.withgale.com) 2. Go to **Settings** → **Webhooks** 3. Click **Add Endpoint** 4. Enter your webhook URL 5. Select the events to subscribe to 6. Copy the secret shown — **it is only displayed once** ## Authentication Every request Gale sends to your endpoint includes a Bearer token in the `Authorization` header: ``` Authorization: Bearer ``` Validate this on your server to confirm the request is genuinely from Gale: ```javascript theme={null} app.post('/webhooks/gale', (req, res) => { const token = req.headers['authorization']?.replace('Bearer ', ''); if (token !== process.env.GALE_WEBHOOK_SECRET) { return res.status(401).send('Unauthorized'); } res.status(200).send('OK'); // process event... }); ``` ## Retry Schedule If your endpoint doesn't return `200`, Gale retries with increasing delays: | Attempt | Delay after previous failure | | --------- | ---------------------------- | | 1st retry | 1 minute | | 2nd retry | 5 minutes | | 3rd retry | 30 minutes | | 4th retry | 2 hours | | 5th retry | 6 hours | After all retries are exhausted the delivery is marked as failed. You can see failed deliveries in the dashboard under **Settings → Webhooks**. ## Webhook Events | Event | When it fires | | ------------------ | ----------------------------- | | `order.completed` | Payment captured successfully | | `order.failed` | Payment failed or declined | | `refund.created` | Refund initiated by merchant | | `refund.succeeded` | Refund processed successfully | | `refund.failed` | Refund attempt failed | ## Event Envelope Every event shares the same top-level structure: ```json theme={null} { "id": "evt_01ABC...", "type": "order.completed", "created_at": "2026-03-31T12:00:00Z", "data": { ... } } ``` Use `id` for deduplication — Gale may deliver the same event more than once during retries. ## Event Payload Examples ### order.completed Fires when payment is captured and the order is complete. Use this to trigger fulfillment. ```json theme={null} { "id": "evt_01ABC...", "type": "order.completed", "created_at": "2026-03-31T12:00:00Z", "data": { "id": "ord_01ABC...", "checkout_id": "cs_abc123", "status": "completed", "is_recurring": false, "payment_status": "captured", "amount": 4995, "currency": "usd", "payment_link_id": "pl_abc123", "client_reference_id": "your-order-123", "reference_id": null, "customer": { "email": "jane@example.com", "name": "Jane Doe" }, "line_items": [ { "product_id": "PROD-001", "name": "Digital Thermometer", "quantity": 1, "unit_amount": 4995, "amount": 4995, "currency": "usd", "is_recurring": false } ], "created_at": "2026-03-31T12:00:00Z" } } ``` `payment_link_id` and `client_reference_id` are present for payment link orders. For checkout session orders, `reference_id` is set instead and `payment_link_id` is null. ### order.failed Fires when payment fails or is declined. ```json theme={null} { "id": "evt_01DEF...", "type": "order.failed", "created_at": "2026-03-31T12:01:00Z", "data": { "id": "ord_01DEF...", "checkout_id": "cs_def456", "status": "failed", "is_recurring": false, "payment_status": "failed", "amount": 4995, "currency": "usd", "payment_link_id": "pl_abc123", "client_reference_id": "your-order-123", "reference_id": null, "failure_reason": "Payment declined", "customer": { "email": "jane@example.com", "name": "Jane Doe" }, "created_at": "2026-03-31T12:01:00Z" } } ``` ### refund.created Fires when a refund is initiated. A `refund.succeeded` or `refund.failed` will follow. ```json theme={null} { "id": "evt_01GHI...", "type": "refund.created", "created_at": "2026-03-31T13:00:00Z", "data": { "id": "ref_01GHI...", "order_id": "ord_01ABC...", "checkout_id": "cs_abc123", "amount": 4995, "currency": "usd", "reason": "Customer request", "payment_link_id": "pl_abc123", "client_reference_id": "your-order-123", "reference_id": null, "customer": { "email": "jane@example.com", "name": "Jane Doe" }, "initiated_at": "2026-03-31T13:00:00Z" } } ``` ### refund.succeeded Fires when the refund is processed. Check `payment_status` to know if the order is fully or partially refunded. ```json theme={null} { "id": "evt_01JKL...", "type": "refund.succeeded", "created_at": "2026-03-31T13:01:00Z", "data": { "id": "ref_01GHI...", "order_id": "ord_01ABC...", "checkout_id": "cs_abc123", "amount": 4995, "currency": "usd", "order_status": "completed", "payment_status": "refunded", "reason": "Customer request", "payment_link_id": "pl_abc123", "client_reference_id": "your-order-123", "reference_id": null, "customer": { "email": "jane@example.com", "name": "Jane Doe" }, "refunded_at": "2026-03-31T13:01:00Z" } } ``` `payment_status` will be `refunded` for a full refund or `partially_refunded` if only part of the order was refunded. ### refund.failed Fires when the refund attempt fails. ```json theme={null} { "id": "evt_01MNO...", "type": "refund.failed", "created_at": "2026-03-31T13:02:00Z", "data": { "id": "ref_01GHI...", "order_id": "ord_01ABC...", "checkout_id": "cs_abc123", "amount": 4995, "currency": "usd", "failure_reason": "Processor declined refund", "payment_link_id": "pl_abc123", "client_reference_id": "your-order-123", "reference_id": null, "customer": { "email": "jane@example.com", "name": "Jane Doe" }, "failed_at": "2026-03-31T13:02:00Z" } } ``` ## Status Fields ### `status` — Fulfillment status | Value | Meaning | | ----------- | ---------------------------------- | | `completed` | Payment captured, ready to fulfill | | `failed` | Payment failed | | `cancelled` | Order cancelled | ### `payment_status` — Payment state | Value | Meaning | | -------------------- | ----------------------------- | | `captured` | Payment successfully captured | | `failed` | Payment failed or declined | | `refunded` | Fully refunded | | `partially_refunded` | Partially refunded | ## Best Practices * **Return 200 immediately**, then process the event asynchronously * **Deduplicate using `id`** — Gale may send the same event more than once during retries * **Don't rely on webhooks alone** — use `GET /api/v2/checkout/{checkout_id}` to poll status if a webhook is missed * **Always validate the Bearer token** before processing ## Missed Webhooks — Status Polling If your server was down or a webhook was missed, you can check order status directly: ```bash theme={null} GET /api/v2/checkout/{checkout_id} ``` [See the endpoint reference →](/api-reference/endpoint/get-checkout) ## Related Resources * [Payment Links](/integration/payment-links) * [Order Object](/api-reference/objects/order) * [Get Checkout Status](/api-reference/endpoint/get-checkout) # Payment Links Source: https://docs.withgale.com/documentation/integration/payment-links Learn how to create one-time and recurring payment links from the Gale Dashboard. ## Overview Payment Links allow you to quickly generate a hosted checkout link from the Gale Dashboard. These links can be shared with customers to accept one-time payments or recurring subscription payments. This guide walks you through: * Creating a **one-time** payment link * Creating a **recurring / subscription** payment link *** ## Prerequisites Before creating a payment link, make sure the following requirements are met: 1. **You must have access to the Gale Dashboard.** 2. **Products Available in Gale** * At least one product must be **synced** from your store\ **OR** * A product must be **manually created** from the Gale Dashboard *** ## Creating a One-Time Payment Link Follow these steps to create a one-time payment link: ### Step 1: Navigate to Payment Links * Log in to the **Gale Dashboard** * Go to **Payment Links** * Click **Create Payment Link** Screenshot 2026 01 30 At 8 14 42 AM *** ### Step 2: Select a Product * In the **Products** section, choose a product from the dropdown * This product will be charged when the payment link is used Screenshot 2026 01 30 At 8 15 42 AM *** ### Step 3: Configure Additional Charges (Optional) In the **Additional Charges** section, you can optionally add: * **Shipping (Flat Fee)** * **Discount** * **Tax Percentage** Screenshot 2026 01 30 At 8 16 17 AM These fields are optional and can be left empty if not required. *** ### Step 4: Add Multiple Shipping Rates (Optional) If you want to offer multiple shipping options: * Add multiple entries under the **Shipping Rates** section * Each rate can have its own label and amount Screenshot 2026 01 30 At 8 16 57 AM *** ### Step 5: Configure Redirect URLs In the **Redirect URLs** section: * Enter a **Success URL**\ → Customer is redirected here after a successful payment * Enter a **Failure URL**\ → Customer is redirected here if the payment fails or is canceled Screenshot 2026 01 30 At 8 17 15 AM *** ### Step 6: Collect Shipping Information (Optional) * If you need to collect shipping details from the customer, make sure the **Collect Shipping Information** checkbox is checked Screenshot 2026 01 30 At 8 18 38 AM *** ### Step 7: Create the Payment Link * Review all details Screenshot 2026 01 30 At 8 19 11 AM * Click **Create Payment Link** * Copy and share the generated payment link with your customer ### Step 8: Client Reference ID (optional) You can simplify reconciliation by appending a `client_reference_id` query parameter to any payment link URL. This is entirely optional — payment links work the same with or without it. When provided, it allows you to associate a payment with a record in your own system, such as a customer ID, order ID, or cart ID. **How it works** After creating a payment link, you can optionally append `client_reference_id` as a query string parameter to the URL: ```text theme={null} https://checkout.withgale.com/payment/pl_M5CS4Y2?client_reference_id={customer_id} ``` If a `client_reference_id` is included: * It will be passed along in the webhook events you receive for that payment. * You can also retrieve the payment status using the `client_reference_id` directly via the API. If omitted, the payment link functions normally without any impact. | Requirements | **Description** | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `client_reference_id` | An optional unique string to associate the payment with your internal systems.
Must be composed of alphanumeric characters, dashes, or underscores, up to 200 characters in length. | *** ## Creating a Recurring / Subscription Payment Link The process for creating a **recurring (subscription)** payment link is almost identical to a one-time payment link, with one additional step during product selection. ### Follow Steps 1–4 Above * Navigate to **Payment Links** * Click **Create Payment Link** * Select a product * Configure optional charges (shipping, discount, tax) *** ### Enable Recurring Subscription When selecting a product: 1. Enable **Make this a recurring subscription** 2. Choose a **Subscription Interval** (e.g. weekly, monthly, yearly) Screenshot 2026 01 30 At 8 19 57 AM *** ### Complete Remaining Steps * Configure **Redirect URLs** * Enable **Collect Shipping Information** if required * Click **Create Payment Link** The generated link will now charge customers on a recurring basis according to the selected interval. *** ## Notes & Best Practices * Ensure redirect URLs are valid and publicly accessible * Use different payment links for different pricing or subscription intervals * Test payment links in a test environment before sharing with customers *** If you have any questions or need help setting up payment links, reach out to the Gale support team. # How Gale Works Source: https://docs.withgale.com/how-it-works Understand the complete Gale Payments flow — from syncing products to completing checkout and fulfilling orders. Gale integrates into your existing checkout flow to help you accept HSA and FSA cards in a compliant, seamless way. We handle product validation, payment authorization, and documentation (like LMNs), all through a secure hosted experience — so you don’t have to. *** ## End-to-End Workflow Gale syncs your product catalog and identifies which items are eligible based on your selected product categories. A new payment method — “Gale HSA/FSA Payments” — appears on your checkout page when eligible items are in the cart. Customers who choose Gale are redirected to a secure, Gale-hosted payment page tailored to HSA/FSA compliance. If the cart contains SIGIS-eligible items, payment is instantly approved.\ For dual-purpose products, the customer completes a short medical survey for LMN review. Once the flow completes, the customer is redirected back to your store.\ The order is marked as **paid** and ready to ship — no manual steps needed. *** ## Summary of the Flow > Product Sync → Payment Method Shown → Payment → Order Completed This flow ensures: * Accurate eligibility validation * Secure handling of healthcare-related payments * A smooth, compliant checkout experience for your customers * Minimal operational burden on your side # Checkout (Hosted Page) Source: https://docs.withgale.com/integration/checkout Redirect customers to Gale's hosted checkout page for seamless HSA/FSA payments # Checkout Integration The most common way to integrate Gale. Create a checkout session via API, redirect your customer to Gale's hosted page, and we handle the rest—including card collection, eligibility detection, and LMN flow for dual-purpose products. ## How It Works 1. **Customer clicks** "Checkout" on your site 2. **Your site** creates a checkout session via `POST /api/v2/checkout` 3. **Gale returns** a `checkout_url` 4. **Your site redirects** customer to the checkout URL 5. **Customer completes** payment on Gale's hosted page 6. **Gale sends** webhook with `order.created` event 7. **Your system** fulfills the order 8. **Customer is redirected** back to your `success_url` ## When to Use Checkout **Using Shopify or WooCommerce?** Install our plugin instead: * [Shopify Plugin](/onboarding/shopify/installation) * [WooCommerce Plugin](/onboarding/woocommerce/installation) The Checkout API is for custom platforms and sites not supported by our plugins. **Perfect for:** * Custom e-commerce platforms * Membership and subscription platforms * SaaS applications * Next.js, React, or custom-built sites * Adding HSA/FSA payments alongside existing payment methods **Use cases:** * Next.js membership site with monthly subscriptions * Custom healthcare e-commerce store * Wellness platform built on custom stack * Telehealth service with custom checkout ## Quick Start ### 1. Create Checkout Session ```javascript theme={null} const createCheckout = async (cart) => { const response = await fetch('https://api.withgale.com/api/v2/checkout', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.GALE_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ reference_id: cart.id, customer: { email: cart.customerEmail, first_name: cart.firstName, last_name: cart.lastName }, line_items: cart.items.map(item => ({ product_id: item.sku, name: item.name, quantity: item.quantity, price: item.price, currency: 'USD' })), shipping_info: cart.shippingAddress, shipping: cart.shipping, tax: cart.tax, success_url: 'https://yoursite.com/order/success', failure_url: 'https://yoursite.com/cart' }) }); const checkout = await response.json(); return checkout.checkout_url; }; ``` ### 2. Redirect Customer ```javascript theme={null} // Redirect to Gale's hosted checkout const checkoutUrl = await createCheckout(cart); window.location.href = checkoutUrl; ``` ### 3. Handle Webhook ```javascript theme={null} app.post('/webhooks/gale', async (req, res) => { const event = req.body; if (event.type === 'order.created') { const order = event.data; // Fulfill the order await fulfillOrder(order.id, { items: order.line_items, shipping: order.customer.shipping_address }); // Send confirmation email await sendEmail({ to: order.customer.email, subject: `Order ${order.order_number} Confirmed`, template: 'order_confirmation' }); } res.status(200).send('OK'); }); ``` ## Integration Examples ### Next.js Membership Site ```typescript theme={null} // app/checkout/route.ts import { NextResponse } from 'next/server'; export async function POST(request: Request) { const { tier, customerId } = await request.json(); const membershipPlans = { basic: { productId: 'BASIC-001', price: 2900 }, premium: { productId: 'PREMIUM-001', price: 4900 }, elite: { productId: 'ELITE-001', price: 9900 } }; const plan = membershipPlans[tier]; const response = await fetch('https://api.withgale.com/api/v2/checkout', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.GALE_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ customer: await getCustomer(customerId), line_items: [{ product_id: plan.productId, name: `${tier} Membership`, quantity: 1, price: plan.price, currency: 'USD' }], payment_type: 'subscription', subscription: { interval: 'monthly' }, success_url: `${process.env.BASE_URL}/welcome?tier=${tier}`, failure_url: `${process.env.BASE_URL}/pricing` }) }); const checkout = await response.json(); return NextResponse.json({ url: checkout.checkout_url }); } ``` ### React E-commerce Checkout ```jsx theme={null} function CheckoutButton({ cart }) { const handleCheckout = async () => { const response = await fetch('/api/create-checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ cart }) }); const { checkout_url } = await response.json(); window.location.href = checkout_url; }; return ( ); } ``` ### Shopify Integration ```javascript theme={null} // Add Gale as payment option const addGalePaymentMethod = () => { // In checkout page const galeButton = document.createElement('button'); galeButton.textContent = 'Pay with HSA/FSA'; galeButton.onclick = async () => { const cart = await fetch('/cart.js').then(r => r.json()); const checkout = await createGaleCheckout({ customer: { email: cart.customer.email, first_name: cart.customer.first_name, last_name: cart.customer.last_name }, line_items: cart.items.map(item => ({ product_id: item.sku, name: item.title, quantity: item.quantity, price: item.price, currency: 'USD' })), success_url: window.location.origin + '/thank-you', failure_url: window.location.origin + '/cart' }); window.location.href = checkout.checkout_url; }; document.querySelector('.payment-methods').appendChild(galeButton); }; ``` ## What Gale Handles When customers reach the hosted checkout page, Gale automatically: 1. **Collects payment information** - Secure card entry 2. **Detects HSA/FSA eligibility** - Real-time SIGIS verification 3. **Handles dual-purpose items** - LMN flow when required 4. **Processes payment** - Charges appropriate payment method(s) 5. **Creates order** - Sends webhook notification to your system ## Checkout URL Customization Pre-fill customer information to streamline checkout: ```javascript theme={null} { customer: { email: "customer@example.com", first_name: "Jane", last_name: "Doe", phone: "+1-555-123-4567" }, shipping_info: { address_line_1: "123 Main St", city: "New York", state: "NY", postal_code: "10001", country: "US" } } ``` Customer only needs to enter payment details. ## Subscription Support For recurring payments, add subscription parameters: ```javascript theme={null} { payment_type: "subscription", subscription: { interval: "monthly", interval_count: 1, trial_period_days: 14 } } ``` ## Testing Use test API keys and test cards: ```javascript theme={null} // Test card numbers const testCards = { success: '4111111111111111', decline: '4000000000000002', requiresAuth: '4000002500003155' }; ``` See [Test Cards](/resources/test-cards) for full list. ## Related Resources * [Checkout API Reference](/api-reference/endpoint/create-checkout-v2) * [Webhooks](/developer-manual/webhooks) * [Order Object](/api-reference/objects/order) ## Support Questions about checkout integration? * 📧 Email: [support@withgale.com](mailto:support@withgale.com) * 💬 [Contact Support](https://www.withgale.com/contact/support) # Custom API Integration Source: https://docs.withgale.com/integration/custom-api Full control over checkout experience with complete API access - perfect for mobile apps and custom platforms # Custom API Integration Build your own checkout experience with complete control using Gale's APIs. Perfect for mobile apps, custom platforms, and merchants who need full flexibility. ## When to Use Custom API Perfect for you if: * You're building a mobile app (iOS, Android, React Native) * You need complete UI control over checkout * You want to manage subscriptions externally * You require advanced features (batch operations, custom flows) * You have development resources for custom implementation ## Architecture Overview The custom API integration follows this flow: 1. **Create Products** - Sync your products with Gale's Product API 2. **Check Eligibility** - Verify HSA/FSA eligibility via Eligibility API 3. **Create Checkout** - Create checkout session via Checkout API 4. **Redirect/Open** - Send customer to Gale's hosted checkout page 5. **Process Payment** - Gale processes payment securely 6. **Webhook** - Receive webhook notification when payment completes 7. **Fulfill Order** - Your backend fulfills the order ## Implementation Guide ### Step 1: Product Management Sync your products with Gale and verify HSA/FSA eligibility: ```bash theme={null} POST /api/v2/products ``` ```json theme={null} { "name": "Digital Blood Pressure Monitor", "description": "FDA-approved automatic BP monitor with Bluetooth connectivity and memory storage for tracking health metrics.", "price": 4995, "currency": "USD", "merchant_product_id": "BP-MONITOR-001", "upc_code_or_gtin": "14567890123456", "images": [ "https://yourcdn.com/bp-monitor-front.jpg", "https://yourcdn.com/bp-monitor-side.jpg" ], "metadata": { "category": "medical_devices", "brand": "HealthTech Pro" } } ``` **Response includes automatic eligibility:** ```json theme={null} { "success": true, "data": { "id": 12345, "name": "Digital Blood Pressure Monitor", "eligibility": { "hsa_fsa_eligible": true, "message": "HSA/FSA accepted" }, "price": 4995 } } ``` [Product API Reference →](/api-reference/endpoint/create-product-v2) ### Step 2: Check Eligibility (Optional) Verify eligibility before adding to cart: ```bash theme={null} POST /api/v2/products/check-eligibility ``` ```json theme={null} { "merchant_product_id": "BP-MONITOR-001" } ``` **Response:** ```json theme={null} { "success": true, "data": { "product_id": "BP-MONITOR-001", "hsa_fsa_eligible": true, "message": "HSA/FSA accepted" } } ``` ### Step 3: Create Checkout Session When customer checks out, create a checkout session: ```bash theme={null} POST /api/v2/checkout ``` ```json theme={null} { "reference_id": "mobile-cart-uuid-123", "customer": { "email": "jane@example.com", "first_name": "Jane", "last_name": "Doe", "phone": "+1-555-123-4567" }, "line_items": [ { "product_id": "BP-MONITOR-001", "name": "Digital Blood Pressure Monitor", "quantity": 1, "price": 4995, "currency": "USD" } ], "shipping_info": { "address_line_1": "123 Main St", "address_line_2": "Apt 4B", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" }, "shipping": 995, "tax": 410, "success_url": "myapp://checkout/success", "failure_url": "myapp://checkout/cancel", "metadata": { "platform": "mobile_app" } } ``` **Response:** ```json theme={null} { "success": true, "message": "Checkout created", "data": { "checkout_id": "cs_a1b2c3...", "checkout_url": "https://checkout.withgale.com/checkout/cs_a1b2c3...?token=...", "status": "open", "expires_at": "2026-02-26T14:30:00Z" }, "metadata": { "request_id": "..." } } ``` [Checkout API Reference →](/api-reference/endpoint/create-checkout-v2) ### Step 4: Handle Checkout **Option A: Use Gale's Hosted Checkout (Recommended)** Redirect to `checkout_url` for payment. Customer completes payment on Gale's secure page. **Option B: Embedded Checkout (Alternative)** For a more seamless experience, embed the checkout in a webview or iframe within your app. See [Embedded Checkout Guide](/integration/embedded-checkout). ### Step 5: Handle Payment Confirmation Use webhooks to receive real-time payment confirmation (recommended) or poll the Order API if needed: ```bash theme={null} GET /api/v2/orders/{order_id} ``` **Response:** ```json theme={null} { "id": "ord_abc123", "status": "completed", "customer": { "email": "jane@example.com", "first_name": "Jane", "last_name": "Doe" }, "line_items": [...], "total": 6400, "created_at": "2025-10-18T15:30:00Z" } ``` ### Step 6: Handle Webhooks Configure webhook endpoint to receive real-time updates: ```javascript theme={null} // Node.js/Express example app.post('/webhooks/gale', async (req, res) => { const event = req.body; // Authenticate the delivery: Gale sends your endpoint secret as a Bearer token const token = req.headers['authorization']?.replace('Bearer ', ''); if (token !== process.env.GALE_WEBHOOK_SECRET) { return res.status(401).send('Invalid webhook token'); } switch (event.type) { case 'order.completed': await handleOrderCompleted(event.data); break; case 'order.failed': await handleOrderFailure(event.data); break; case 'refund.succeeded': await handleRefundSucceeded(event.data); break; } res.status(200).send('OK'); }); ``` [Webhook Documentation →](/developer-manual/webhooks) ## Subscription Support Gale handles subscriptions for you. When creating a checkout session, pass subscription parameters: ```json theme={null} { "reference_id": "sub-vitamins-123", "customer": {...}, "line_items": [{ "product_id": "MONTHLY_VITAMINS", "name": "Monthly Vitamin Subscription", "quantity": 1, "price": 2999, "currency": "USD" }], "payment_type": "subscription", "subscription": { "interval": "monthly", "interval_count": 1, "trial_period_days": 7 } } ``` Subscription checkout is currently in Beta. Gale will automatically: * Collect payment method * Charge customer monthly * Send webhooks for each payment * Handle failed payments and retries See [Subscription API Reference](/api-reference/endpoint/create-subscription) for more details. ## Batch Operations ### Batch Eligibility Check Check multiple products at once: ```bash theme={null} POST /api/v2/products/batch-eligibility ``` See [Check Eligibility API](/api-reference/endpoint/check-eligibility) for details. ## Advanced Features ### Idempotency Prevent duplicate operations using idempotency keys: ```bash theme={null} POST /api/v2/checkout Idempotency-Key: unique-checkout-12345 ``` ### Rate Limiting See [Rate Limits](/developer-manual/rate-limits) for handling rate limits. ## Best Practices Never hardcode API keys in mobile apps. Proxy through your backend. Implement proper error handling for all API calls Don't rely solely on polling - use webhooks for reliability Cache product eligibility to reduce API calls ## Error Handling ```javascript theme={null} try { const response = await fetch('https://api.withgale.com/api/v2/checkout', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}` }, body: JSON.stringify(checkoutData) }); if (!response.ok) { const error = await response.json(); switch (error.error.code) { case 'validation_error': // Handle validation errors console.error('Validation failed:', error.errors); break; case 'checkout_expired': // Checkout expired - create new one break; case 'product_not_eligible': // Product not HSA/FSA eligible break; default: console.error('API Error:', error.error.message); } } return response.json(); } catch (error) { console.error('Network error:', error); } ``` ## Testing ### Test Environment Always use test keys during development: ``` Authorization: Bearer glm_test_YOUR_TEST_KEY ``` ### Test Scenarios 1. **Successful payment**: Use test card `4111111111111111` 2. **Card declined**: Use test card `4000000000000002` 3. **Expired session**: Create checkout and wait 24+ hours 4. **Mixed eligibility**: Create checkout with eligible and non-eligible products [See all test cards →](/resources/test-cards) ## API Reference Complete API documentation: * [Product APIs](/api-reference/endpoint/create-product-v2) * [Checkout API](/api-reference/endpoint/create-checkout-v2) * [Order APIs](/api-reference/endpoint/get-order) * [Eligibility APIs](/api-reference/endpoint/check-eligibility) * [Subscription APIs](/api-reference/endpoint/create-subscription) ## Next Steps Set up webhook endpoints Understand data structures Test payment scenarios Handle errors properly ## Support Building a custom integration? * 📧 Email: [support@withgale.com](mailto:support@withgale.com) * 📚 [Full API Reference](/api-reference/introduction) * 💬 [Contact Support](https://www.withgale.com/contact/support) # Embedded Checkout Source: https://docs.withgale.com/integration/embedded-checkout Keep customers on your site with iframe-embedded checkout - the perfect balance of simplicity and branding # Embedded Checkout Integration Embed Gale's secure checkout directly on your website using an iframe. Customers stay on your domain while Gale handles payment processing, eligibility verification, and compliance. ## When to Use Embedded Checkout Perfect for you if: * You want customers to stay on your website during checkout * You need a branded checkout experience * You're building a platform or multi-tenant solution * You want Gale to handle checkout UI and compliance * You can integrate via API to create checkout sessions ## How It Works 1. **Customer adds items** to cart on your website 2. **Your site** creates a checkout session via `POST /api/v2/checkout` 3. **Gale API returns** a `checkout_url` 4. **Your site embeds** the checkout URL in an iframe 5. **Customer completes** payment in the iframe (stays on your domain) 6. **Gale processes** the payment securely 7. **Gale sends** webhook notification with `order.created` event 8. **Your site shows** confirmation to customer ## Implementation Steps When customer is ready to checkout, create a checkout session with their items Use the returned `checkout_url` in an iframe on your checkout page Listen for webhooks or postMessage events to confirm payment Process the order and show confirmation to customer ## Step 1: Create Checkout Session Create a checkout session with customer info and line items. > All monetary amounts are integers in cents (e.g., 2995 = \$29.95). ```bash theme={null} POST /api/v2/checkout ``` ```json theme={null} { "customer": { "email": "customer@example.com", "first_name": "Jane", "last_name": "Doe", "phone": "+1-555-123-4567" }, "reference_id": "your-cart-123", "line_items": [ { "product_id": "PROD-001", "name": "Digital Thermometer", "quantity": 1, "price": 2995, "currency": "USD" } ], "shipping_info": { "address_line_1": "123 Main St", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" }, "shipping": 995, "tax": 245, "success_url": "https://yoursite.com/checkout/success", "failure_url": "https://yoursite.com/checkout/cancel", "metadata": { "platform": "your_platform" } } ``` **Response:** ```json theme={null} { "checkout_id": "checkout_abc123xyz", "checkout_url": "https://checkout.withgale.com/c/checkout_abc123xyz", "status": "open", "expires_at": "2025-10-19T14:30:00Z", "created_at": "2025-10-18T14:30:00Z" } ``` ## Step 2: Embed Checkout Use the `checkout_url` in an iframe on your page: ```html theme={null} Checkout - Your Store

Complete Your Purchase

```
```jsx theme={null} import { useEffect, useState } from 'react'; function GaleCheckout({ cartId }) { const [checkoutUrl, setCheckoutUrl] = useState(null); useEffect(() => { // Create checkout session const createCheckout = async () => { const response = await fetch('https://api.withgale.com/api/v2/checkout', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.GALE_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ // checkout data }) }); const { checkout_url } = await response.json(); setCheckoutUrl(checkout_url); }; createCheckout(); }, []); useEffect(() => { // Listen for checkout events const handleMessage = (event) => { if (event.origin !== 'https://checkout.withgale.com') return; if (event.data.type === 'checkout.completed') { // Handle success window.location.href = `/success?order=${event.data.order_id}`; } }; window.addEventListener('message', handleMessage); return () => window.removeEventListener('message', handleMessage); }, []); if (!checkoutUrl) return
Loading checkout...
; return (

Complete Your Purchase