Create Checkout Session
Create a checkout session and get a hosted checkout URL
POST
/
api
/
v2
/
checkout
Create Checkout Session
curl --request POST \
--url https://api.example.com/api/v2/checkoutimport requests
url = "https://api.example.com/api/v2/checkout"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.example.com/api/v2/checkout', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/v2/checkout",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/v2/checkout"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/v2/checkout")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/v2/checkout")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_bodyCreate 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
Authorization: Bearer glm_test_YOUR_API_KEY
Request Body
{
"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
{
"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": "..." }
}
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
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
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 |
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 anorder.created webhook:
{
"type": "order.created",
"data": {
"id": "ord_abc123",
"checkout_id": "01HXYZ...",
"reference_id": "your-order-123",
"status": "completed",
"customer": {
"email": "jane@example.com"
}
}
}
Errors
| Status Code | Error Code | Description |
|---|---|---|
| 400 | bad_request | Missing or invalid parameters |
| 401 | unauthorized | Invalid API key |
| 422 | validation_error | Field validation failed |
Related
Previous
Get Checkout StatusRetrieve the status and order details for a checkout session or payment link
Next
⌘I
Create Checkout Session
curl --request POST \
--url https://api.example.com/api/v2/checkoutimport requests
url = "https://api.example.com/api/v2/checkout"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.example.com/api/v2/checkout', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/v2/checkout",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/v2/checkout"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/v2/checkout")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/v2/checkout")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_body