Early Access

The commerce API
you've been waiting for.

Orders, payments, subscriptions, and refunds in one API. Built by ex-Square engineers. Join the waitlist to get early access.

No credit card requiredFree sandbox accessBuilt by ex-Square engineers
For AI Agents

Try the sandbox now

Don't want to wait? Feed this document to your AI agent and start building against our sandbox API right now. Includes a pre-provisioned API key.

llm.txt
# Flint Pay — Sandbox API Reference (llm.txt)

> This document is designed for AI agents and LLMs integrating with the Flint Pay Sandbox API.
> All fields use camelCase in JSON. All requests are POST with Content-Type: application/json.

## Base URL & Auth

```
Base:  https://api.staging.withflintpay.com
Auth:  Authorization: Bearer <your_sandbox_api_key>
Path:  POST /<package>.<Service>/<Method>
```

### Sandbox Demo Key (read/write to all resources)

```
flint_test_9cf074b6f47381d2cad56992bcb1abf3915940f84ff1158ce3db56503208e3ed
```

## Money Fields

Integer cents with currency code. Three types:
- `Money`: amount >= 0 (most fields)
- `PositiveMoney`: amount >= 1 (tips, refund amounts)
- `SignedMoney`: can be negative (balances, deltas)

```json
{ "amount": 1500, "currency": "USD" }   // = $15.00
```

## ID Prefixes

`ord_` order, `li_` line item, `cus_` customer, `pi_` payment intent, `pm_` payment method, `item_` catalog item, `cpn_` coupon, `ref_` refund, `sub_` subscription, `plan_` plan, `cs_` checkout session, `mer_` merchant

Embedded sub-objects (tip, applied_discount) have no prefix.

## Error Responses

Errors return a Connect RPC status code with a structured body:

```json
{
  "code": "FIELD_REQUIRED",
  "message": "The 'order_id' field is required",
  "field": "order_id"
}
```

Common status codes:
- `invalid_argument` (400): validation errors — FIELD_REQUIRED, INVALID_ID, CURRENCY_MISMATCH, ORDER_CLOSED, COUPON_EXPIRED, COUPON_NOT_STACKABLE, LINE_ITEM_INVALID_QUANTITY
- `not_found` (404): RESOURCE_NOT_FOUND, COUPON_NOT_FOUND, DISCOUNT_NOT_FOUND
- `already_exists` (409): ORDER_CONFLICT (concurrent modification — refetch and retry)
- `unauthenticated` (401): missing/invalid API key
- `permission_denied` (403): insufficient scope or wrong merchant
- `unavailable` (503): PAYMENT_PROCESSOR_ERROR (retryable), PAYMENT_FAILED (not retryable without new payment method)

## Pagination (all List endpoints)

`pageSize`: 1-100 (default 20). `pageToken`: opaque string from `nextPageToken` in previous response. Empty `nextPageToken` = no more pages.

## FieldMask Updates

Update RPCs use `updateMask` (camelCase, comma-separated paths). Only fields in the mask are modified:

```json
{
  "orderId": "ord_xxx",
  "updateMask": "buyerNote,metadata",
  "buyerNote": "Rush order",
  "metadata": { "key": "value" }
}
```

## Metadata (all resources)

Max 50 keys. Key: 1-255 chars. Value: max 2048 chars.

---

## Orders — `flint.v1.commerce.OrderService`

### Methods

| Method | Description |
|--------|-------------|
| CreateOrder | Create order. Optional: `customerId`, `lineItems`, `tips`, `discounts`, `buyerNote`, `merchantNote` |
| GetOrder | Get by `orderId` |
| UpdateOrder | Update `customerName`, `buyerNote`, `merchantNote`, `metadata`. Requires `updateMask` |
| ListOrders | Filter: `customerId`, `status`, `orderNumber`, `source`, `subscriptionId`, `query`, date ranges. Sort: `CREATED_AT`, `UPDATED_AT`, `DUE_MONEY` |
| CloseOrder | Close an OPEN order with no balance |
| PayOrder | Confirm all attached payment intents. Fields: `orderId`, optional `paymentIntentIds`, `idempotencyKey` |
| AddLineItems | Add `lineItems` to order |
| UpdateLineItem | Update by `orderLineItemId`. Catalog-linked: only `quantity`. Ad-hoc: `quantity`, `name`, `unitPriceMoney`, etc. |
| RemoveLineItems | Remove by `orderLineItemIds` (not `lineItemIds`) |
| AddTips | Add tips to order |
| UpdateTip | Update by `orderTipId` |
| RemoveTips | Remove by `orderTipIds` (not `tipIds`) |
| ApplyCoupon | Apply by `couponCode` OR `couponId` (oneof, exactly one required) |
| RemoveDiscounts | Remove by `appliedDiscountIds` (not `discountIds`). Cannot remove redeemed discounts |
| ApplyTax | Apply tax using `taxAddress` (can be a zip code). Must re-run after line item changes |

### Line Item Input

```json
{
  "name": "Widget",         // required, max 255 chars
  "quantity": "1",           // required, >= 1, string
  "unitPriceMoney": { "amount": 1500, "currency": "USD" },  // required, Money (>= 0)
  "itemId": "item_xxx",     // optional — links to catalog item
  "description": "...",     // optional
  "sku": "...",             // optional
  "metadata": {}            // optional
}
```

### Tip Input

`oneof tipValue` — exactly one required:
- `amountMoney`: PositiveMoney (>= 1 cent)
- `percent`: whole number 1.0–100.0 (NOT a fraction)

### Discount Input (on CreateOrder)

`oneof source`:
- `coupon`: `{ "couponId": "cpn_xxx" }` or `{ "couponCode": "SAVE10" }`
- `manual`: `{ "name": "...", "amountMoney": {...}, "scope": "DISCOUNT_SCOPE_ORDER" }`

### Order Status & Allowed Mutations

| Status | Allowed |
|--------|---------|
| `OPEN` | All mutations (line items, tips, discounts, payments, close) |
| `PAID` | Refunds and metadata only |
| `PARTIALLY_REFUNDED` | Additional refunds, metadata only |
| `CLOSED`, `REFUNDED` | No mutations |

### Response: Net Amounts

The `netAmounts` object on every Order response:
- `subtotalMoney`: sum of line item gross amounts
- `discountMoney`: sum of applied discounts
- `taxMoney`: sum of taxes
- `tipMoney`: sum of tips
- `paidMoney`: sum of succeeded payments
- `refundedMoney`: sum of succeeded refunds
- `balanceMoney` (SignedMoney): positive = owed, zero = settled, negative = overpaid

Each line item also has computed `grossMoney`, `discountMoney`, `taxMoney`, `netMoney`.

### Source Enum (shared across Orders, PaymentIntents, CheckoutSessions)

`SOURCE_API`, `SOURCE_CHECKOUT`, `SOURCE_VIRTUAL_TERMINAL`, `SOURCE_PAYMENT_LINK`, `SOURCE_POS`, `SOURCE_SUBSCRIPTION`

---

## Customers — `flint.v1.customers.CustomerService`

### Methods

| Method | Description |
|--------|-------------|
| CreateCustomer | Required: `name` (1-255), `email` (valid format). Optional: `phoneNumber`, `billingAddress`, `shippingAddress`, `merchantCustomerId`, `buyerNote` |
| GetCustomer | Get by `customerId` |
| GetCustomerByEmail | Get by `email` (exact match, scoped to merchant) |
| UpdateCustomer | Mutable: `name`, `billingAddress`, `shippingAddress`, `merchantCustomerId`, `buyerNote`, `phoneNumber`, `merchantNote`, `metadata`. Email is IMMUTABLE |
| ListCustomers | Filter: `query` (search name/email/phone), `email` (exact match), date ranges. Sort: `NAME`, `EMAIL`, `CREATED_AT`, `UPDATED_AT` |

### Address Format

```json
{ "line1": "123 Main St", "line2": "Apt 4", "city": "NYC", "state": "NY", "postalCode": "10001", "country": "US" }
```

`country` is 2-char ISO code.

---

## Items — `flint.v1.commerce.ItemService`

### Methods

| Method | Description |
|--------|-------------|
| CreateItem | Required: `name`, `unitPriceMoney` (Money, allows 0). Optional: `type`, `sku`, `description`, `categories`, `imageUrl`, `quantityAvailable`, `enforceInventoryLimit`, `taxable`, `taxCategory` |
| GetItem | Get by `itemId` |
| GetItemBySKU | Get by `sku` (exact match, scoped to merchant) |
| UpdateItem | Mutable via FieldMask: `name`, `description`, `unitPriceMoney`, `sku`, `imageUrl`, `taxable`, `taxCategory`, `enforceInventoryLimit`, `status`, `metadata`. NOT `quantityAvailable` or `categories` |
| DeleteItem | Permanent delete |
| ListItems | Filter: `type`, `status`, `onlyInStock`, `category`, `query` (name/SKU search), date ranges. Sort: `NAME`, `CREATED_AT`, `UPDATED_AT`, `UNIT_PRICE` |
| AdjustInventory | `itemId`, `quantityDelta` (nonzero int, can be negative), optional `note`, `changeReason` |
| AddItemCategories | Add categories by string array |
| RemoveItemCategories | Remove categories by string array |
| ReplaceItemCategories | Replace all categories (empty array clears) |

### Item Enums

- **ItemType**: `ITEM_TYPE_PRODUCT`, `SERVICE`, `FEE`, `DIGITAL`
- **ItemStatus**: `ITEM_STATUS_ACTIVE`, `INACTIVE` (reversible), `DELETED` (permanent)
- **InventoryChangeReason**: `MANUAL_ADJUSTMENT`, `ORDER_FULFILLMENT`, `ORDER_CANCELLATION`, `ORDER_REFUND`, `RECEIVED_STOCK`, `INVENTORY_COUNT`, `DAMAGE`, `THEFT`, `LOSS`

---

## Coupons — `flint.v1.commerce.CouponService`

### Methods

| Method | Description |
|--------|-------------|
| CreateCoupon | Required: `name`, one of `percentOff` (0.01-100.0) or `amountOffMoney`. Optional: `couponCode` (omit = codeless coupon), `maxUses`, `expiresAt`, `isStackable`, `minimumPurchaseAmount`, `limitToItemIds` |
| GetCoupon | Get by `couponId` |
| GetCouponByCode | Get by `couponCode` (returns any status except DELETED) |
| UpdateCoupon | Mutable: `name`, `couponCode`, `maxUses`, `expiresAt`, `status` (`COUPON_MUTABLE_STATUS_ACTIVE`/`DISABLED`), `isStackable`, `minimumPurchaseAmount`, `percentOff`/`amountOffMoney`, `metadata`. NOT `limitToItemIds` (use RPCs below) |
| ListCoupons | Filter: `status`, `query` (name/code search), `limitToItemId`, expiry/date ranges. Sort: `NAME`, `CREATED_AT`, `UPDATED_AT`, `EXPIRES_AT` |
| DeleteCoupon | Permanent delete |
| AddCouponLimitToItems | Add item restrictions |
| RemoveCouponLimitToItems | Remove item restrictions |
| ReplaceCouponLimitToItems | Replace all (empty = universal coupon) |

### Coupon Status (computed, read-only)

`COUPON_STATUS_ACTIVE`, `DISABLED` (merchant-toggled, reversible), `EXPIRED` (past expiresAt), `EXHAUSTED` (usesCount >= maxUses), `DELETED`

Discount value is oneof: set `percentOff` OR `amountOffMoney`, never both.

---

## Payments — `flint.v1.payments.PaymentIntentService`

### Methods

| Method | Description |
|--------|-------------|
| CreatePaymentIntent | Required: `orderId`. Optional: `amountMoney` (omit to auto-derive from order balance), `autoConfirm`, `paymentMethodTypes`, `digitalWallets`, `tipMoney`, `statementDescriptor` (max 22 chars), `receiptEmail`, `paymentMethodId` (saved pm_), `customerId` |
| GetPaymentIntent | Get by `paymentIntentId` |
| UpdatePaymentIntent | Mutable (varies by status). FieldMask: `amountMoney`, `sourceId`, `customerId`, `tipMoney`, `receiptEmail`, `metadata` |
| ConfirmPaymentIntent | Confirm after attaching source. Fields: `paymentIntentId`, optional `sourceId`, `idempotencyKey` |
| CapturePaymentIntent | Capture an authorized payment. Optional `amountMoney` for partial capture |
| ListPaymentIntents | Filter: `status`, `orderId`, `customerId`, `source`, `minAmount`/`maxAmount`, `query`, date ranges. Sort: `CREATED_AT`, `UPDATED_AT`, `AMOUNT` |
| CancelPaymentIntent | Cancel a non-terminal payment intent |

### Payment Status State Machine

```
REQUIRES_PAYMENT_METHOD → REQUIRES_CONFIRMATION → PROCESSING → SUCCEEDED
                                                             → REQUIRES_ACTION (3DS)
                                                             → REQUIRES_CAPTURE (auth-only)
REQUIRES_CAPTURE → SUCCEEDED (via CapturePaymentIntent)
Any non-terminal → CANCELED | EXPIRED (24h timeout)
```

Terminal: `SUCCEEDED`, `CANCELED`, `EXPIRED`

### Mutability by Status

- `REQUIRES_PAYMENT_METHOD`: all fields mutable
- `REQUIRES_CONFIRMATION`: only metadata, customerId, tipMoney, receiptEmail
- `PROCESSING` or terminal: nothing mutable

### Response Fields

Key response fields: `status`, `amountMoney`, `tipMoney`, `paymentMethodLastFour`, `paymentMethodBrand`, `processorDetails.stripe.clientSecret` (needed for frontend Stripe.js), `nextActions` (agent hints for next steps).

### Payment Method Types & Wallets

- **PaymentMethodType**: `PAYMENT_METHOD_TYPE_CARD`, `AFFIRM`, `LINK`
- **DigitalWallet**: `DIGITAL_WALLET_APPLE_PAY`, `GOOGLE_PAY`
- **CardBrand**: `CARD_BRAND_VISA`, `MASTERCARD`, `AMEX`, `DISCOVER`, `DINERS`, `JCB`, `UNIONPAY`

---

## Payment Methods — `flint.v1.payments.PaymentMethodService`

### Methods

| Method | Description |
|--------|-------------|
| SavePaymentMethod | Create a setup intent for `customerId`. Returns `processorDetails.stripe.clientSecret` for frontend `stripe.confirmCardSetup()` |
| GetPaymentMethod | Get by `paymentMethodId`. Card details populated only after Stripe webhook confirms setup |
| ListPaymentMethods | Required: `customerId`. Returns ACTIVE methods by default |
| RemovePaymentMethod | Remove by `paymentMethodId` |
| SetDefaultPaymentMethod | Set default for `customerId`. Must be ACTIVE and belong to that customer |

### Lifecycle

1. `SavePaymentMethod(customerId)` → get `clientSecret`
2. Frontend calls `stripe.confirmCardSetup(clientSecret)`
3. Stripe webhook fires → card details saved
4. `GetPaymentMethod` now returns `card` with `last4`, `brand`, `expMonth`, `expYear`, optional `wallet`

**PaymentMethodStatus**: `ACTIVE`, `EXPIRED`, `REMOVED`

---

## Refunds — `flint.v1.commerce.RefundService`

### Methods

| Method | Description |
|--------|-------------|
| CreateRefund | Target: `orderId` and/or `paymentIntentId` (at least one). Optional: `amountMoney` (omit = full refund). Required: `reason`, `idempotencyKey`. Optional: `reasonMessage` |
| GetRefund | Get by `refundId` |
| UpdateRefund | Only `metadata` is mutable |
| ListRefunds | Filter: `orderId`, `paymentIntentId`, `status`, `customerId`, `reasons`, `minAmount`/`maxAmount`, date ranges. Sort: `CREATED_AT`, `UPDATED_AT`, `AMOUNT` |

### Refund Status

`REFUND_STATUS_PENDING` → `IN_TRANSIT` → `SUCCEEDED` or `FAILED`

### Refund Reason Enum (required)

`REFUND_REASON_DUPLICATE`, `FRAUDULENT`, `REQUESTED_BY_CUSTOMER`, `DEFECTIVE_PRODUCT`, `WRONG_ITEM_SHIPPED`, `NEVER_RECEIVED`, `NOT_AS_DESCRIBED`, `ARRIVED_TOO_LATE`, `CUSTOMER_CHANGED_MIND`, `BETTER_PRICE_FOUND`, `ACCIDENTAL_ORDER`, `OTHER`

---

## Subscriptions — `flint.v1.commerce.SubscriptionService`

### Methods

| Method | Description |
|--------|-------------|
| CreateSubscription | Required: `planId`, `customerId`. Optional: `paymentMethodId` (falls back to customer default), `billingAnchorDay` (1-31, default today) |
| GetSubscription | Get by `subscriptionId` |
| UpdateSubscription | Mutable: `paymentMethodId`, `metadata`. Requires `updateMask` |
| ListSubscriptions | Filter: `status`, `customerId`, `planId` |
| CancelSubscription | Optional `cancelImmediately` (default false = cancel at period end). Returns `contractInfo` if applicable |
| PauseSubscription | Optional `pauseDurationCycles` (omit = indefinite pause) |
| ResumeSubscription | Resume a paused subscription |

### Subscription Status

`SUBSCRIPTION_STATUS_TRIALING`, `ACTIVE`, `PAUSED`, `PAST_DUE`, `CANCELED` (terminal)

### Response Fields

Key fields: `currentPeriodStart`, `currentPeriodEnd`, `nextBillingDate`, `cancelAtPeriodEnd`, `trialEnd`, `pausedAt`, `canceledAt`

---

## Subscription Plans — `flint.v1.commerce.SubscriptionPlanService`

### Methods

| Method | Description |
|--------|-------------|
| CreateSubscriptionPlan | Required: `name`, `billingInterval`, `currency` (3-char ISO). Optional: `billingIntervalCount` (default 1), `lineItems`, `setupFeeMoney`, `trialPeriodDays`, `description`, `contractTermMonths`, `earlyTerminationFeeMoney` |
| GetSubscriptionPlan | Get by `planId` (returns archived plans too) |
| UpdateSubscriptionPlan | Mutable: `name`, `description`, `lineItems`, `setupFeeMoney`, `trialPeriodDays`, `metadata`, `contractTermMonths`, `earlyTerminationFeeMoney`. Immutable: `billingInterval`, `billingIntervalCount`, `currency` |
| ListSubscriptionPlans | Filter: `status`, date ranges. Sort: `NAME`, `CREATED_AT`, `UPDATED_AT` |
| ArchiveSubscriptionPlan | Archive (not delete). Existing subscriptions continue |

### Plan Line Item

Two modes — catalog-linked or ad-hoc:
```json
// Catalog: price resolved from item at billing time
{ "itemId": "item_xxx", "quantity": 1 }

// Ad-hoc: fixed price
{ "name": "Monthly Fee", "unitPriceMoney": { "amount": 2999, "currency": "USD" }, "quantity": 1 }
```

### Billing Interval

`BILLING_INTERVAL_DAILY`, `WEEKLY`, `MONTHLY`, `YEARLY`. Combined with `billingIntervalCount` (e.g., 2 + MONTHLY = every 2 months).

**SubscriptionPlanStatus**: `ACTIVE`, `ARCHIVED`

---

## Checkout Sessions — `flint.v1.checkouts.CheckoutSessionService`

### Methods

| Method | Description |
|--------|-------------|
| CreateCheckoutSession | Oneof: `orderId` (existing order) OR `quickPay` (`{ "name": "...", "amountMoney": {...} }` auto-creates order). Config objects: `theme`, `paymentConfig`, `tipConfig`, `customerConfig`, `legalConfig`, `expirationConfig`, `taxConfig`, `redirects` |
| GetCheckoutSession | Get by `checkoutSessionId`. Response includes `url` (hosted payment page) |
| UpdateCheckoutSession | Only `metadata` is mutable (works in any state including terminal) |
| CloseCheckoutSession | Close with optional `reason` |
| ListCheckoutSessions | Filter: `status`, `orderId`, `source`, `query`, date ranges. Sort: `CREATED_AT`, `UPDATED_AT` |

### Checkout Session Status

`OPEN` → `PAID` (payment succeeded) | `EXPIRED` (timed out) | `CLOSED` (manually closed) | `INVALIDATED` (order modified after session created)

### Key Config Objects

**ExpirationConfig**: `expirationSeconds` (1-86400, default 86400)
**TipConfig**: `isEnabled`, `tipPercentages` (array of 0.01-100.0), `defaultTipPercentage`, `isCustomTipEnabled`, `isSmartTipsEnabled`, `smartTipAmounts`
**CustomerConfig**: `customerId` (auto-fill), `prefilledCustomerInfo`, `enableAddressAutocomplete`
**Redirects**: `successRedirectUrl`, `cancelRedirectUrl`, `onLoadRedirectUrl`
**PaymentConfig**: `allowedPaymentMethodTypes`, `allowedDigitalWallets`, `statementDescriptor`

---

## Settings — `flint.v1.settings.SettingsService`

### Methods

| Method | Description |
|--------|-------------|
| GetSettings | Get raw settings at one scope. Required: `scope` |
| UpdateSettings | Update settings. Config sections are top-level fields (NOT nested under `settings`). Requires `scope` + `updateMask` |
| GetEffectiveSettings | Get merged/inherited settings. Optional `locationId` |

### Scope Hierarchy (most specific wins)

`SETTINGS_SCOPE_ORGANIZATION` < `SETTINGS_SCOPE_MERCHANT` < `SETTINGS_SCOPE_LOCATION`

### Config Sections

- **tipping**: `isEnabled`, `tipPercentages`, `defaultTipPercentage`, `isCustomTipEnabled`, `isSmartTipsEnabled`, `smartTipAmounts`
- **checkout**: `allowedPaymentMethodTypes`, `allowedDigitalWallets`, `isCustomerEmailRequired`, `statementDescriptor` (max 22), `defaultExpirationSeconds`
- **tax**: `isEnabled`
- **receipt**: `headerText`, `footerText`, `isItemized`, `isAutoEmailEnabled`
- **inventory**: `lowStockThreshold` (0 = disabled)
- **branding**: `primaryColor` (hex #RRGGBB), `accentColor`
- **legal**: `termsOfServiceUrl`, `requiresTermsOfService`, `refundPolicyUrl`
- **subscription**: `dunningEndAction` (`CANCEL`/`PAUSE`), `dunningRetryDays` (1-90)

IMPORTANT: config sections go directly on the request, NOT nested under a `settings` key:
```json
// Correct:
{ "scope": "SETTINGS_SCOPE_MERCHANT", "checkout": { "statementDescriptor": "Acme" }, "updateMask": "checkout.statementDescriptor" }
```

---

## Accounts

### Merchants — `flint.v1.accounts.merchants.MerchantService`

| Method | Description |
|--------|-------------|
| GetMerchant | Get merchant details |
| UpdateMerchant | Update merchant profile |

### Users — `flint.v1.accounts.UserService`

| Method | Description |
|--------|-------------|
| ListUsers | List users |
| GetUser | Get user by ID |

### API Keys — `flint.v1.accounts.APIKeyService`

| Method | Description |
|--------|-------------|
| CreateAPIKey | Create a new API key |
| RevokeAPIKey | Revoke an API key |
| ListAPIKeys | List API keys |

---

## Idempotency

RPCs that accept `idempotencyKey` will return the cached response for duplicate requests with the same key. Use UUIDs. Applies to: CreateOrder, CreatePaymentIntent, ConfirmPaymentIntent, CapturePaymentIntent, CreateRefund, PayOrder, CreateCustomer.

## Response Metadata

Every response includes a `meta` object with `requestId` (for debugging) and `idempotencyReplayed` (bool, true if response was from cache).

## Important: merchantId is Server-Derived

Never set `merchantId` in request bodies — it is always derived from the authenticated API key. If provided and mismatched, returns `permission_denied`.

---

## Bug Reports & Feedback

If you encounter unexpected API behavior, errors that don't match this documentation, or missing functionality, report it at:

https://github.com/flint-llc/flintpay-api-feedback/issues

### How to file a good bug report

1. Click "New issue" and select "Bug Report"
2. Fill in all required fields — incomplete reports are closed without review
3. Required fields:
   - **Service**: which service the bug is in (e.g. OrderService)
   - **Endpoint**: full RPC path (e.g. flint.v1.commerce.OrderService/CreateOrder)
   - **Request body**: the exact JSON you sent (remove API keys)
   - **Response received**: the full error or unexpected response
   - **Expected behavior**: what you expected to happen
4. Check the three preflight boxes confirming it's not a duplicate and no sensitive data is included

### Security vulnerabilities

Do NOT file security issues publicly. Email security@withflintpay.com instead.

### Feature requests

Use the "Feature Request" template on the same repo to suggest new endpoints, fields, or behaviors.

---

## Quick Start Example

### 1. Create a customer

```bash
curl -X POST https://api.staging.withflintpay.com/flint.v1.customers.CustomerService/CreateCustomer \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer flint_test_9cf074b6f47381d2cad56992bcb1abf3915940f84ff1158ce3db56503208e3ed" \
  -d '{"name": "Jane Doe", "email": "jane@example.com"}'
```

### 2. Create an order with line items

```bash
curl -X POST https://api.staging.withflintpay.com/flint.v1.commerce.OrderService/CreateOrder \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer flint_test_9cf074b6f47381d2cad56992bcb1abf3915940f84ff1158ce3db56503208e3ed" \
  -d '{
    "customerId": "cus_xxx",
    "lineItems": [{
      "name": "Premium Widget",
      "quantity": "1",
      "unitPriceMoney": {"amount": 2500, "currency": "USD"}
    }]
  }'
```

### 3. Create a payment intent (amount auto-derived from order balance)

```bash
curl -X POST https://api.staging.withflintpay.com/flint.v1.payments.PaymentIntentService/CreatePaymentIntent \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer flint_test_9cf074b6f47381d2cad56992bcb1abf3915940f84ff1158ce3db56503208e3ed" \
  -d '{"orderId": "ord_xxx"}'
```

### 4. Create a refund (full amount)

```bash
curl -X POST https://api.staging.withflintpay.com/flint.v1.commerce.RefundService/CreateRefund \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer flint_test_9cf074b6f47381d2cad56992bcb1abf3915940f84ff1158ce3db56503208e3ed" \
  -d '{
    "orderId": "ord_xxx",
    "reason": "REFUND_REASON_REQUESTED_BY_CUSTOMER",
    "idempotencyKey": "unique-key-123"
  }'
```

### 5. Create a checkout session (quick pay)

```bash
curl -X POST https://api.staging.withflintpay.com/flint.v1.checkouts.CheckoutSessionService/CreateCheckoutSession \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer flint_test_9cf074b6f47381d2cad56992bcb1abf3915940f84ff1158ce3db56503208e3ed" \
  -d '{
    "quickPay": {"name": "One-time charge", "amountMoney": {"amount": 5000, "currency": "USD"}},
    "redirects": {"successRedirectUrl": "https://example.com/thanks"}
  }'
```

Copy and paste into your AI agent's system prompt or context window to start making API calls.