Build your own customer account

/v1/me is the buyer's own view of their commerce data. Order history, delivery tracking, invoices, subscriptions, saved cards, addresses, and Returns, all authorized by a customer session rather than your merchant key.

The point of the namespace is what it refuses to do. There is no customer_id argument anywhere in it. You cannot ask for another buyer's orders, so you cannot accidentally ship a front end that does.

The whole loop#

1

Authenticate the buyer yourself#

Your login, your session, your user table. Flint is not involved.

2

Mint a customer session on your backend#

Bash
curl -X POST https://api.withflintpay.com/v1/customer-sessions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "customer_id": "cus_1kmn0aExample" }'

Keep secret and refresh_token in your server-side session. Never send them to the browser.

3

Read the buyer's data with the session secret#

Bash
curl https://api.withflintpay.com/v1/me/orders \
  -H "Authorization: Bearer flint_cses_example"

No customer filter. There is nothing to filter by, because the credential already decided.

4

Refresh on expiry, revoke on sign-out#

CUSTOMER_SESSION_EXPIRED means refresh and retry. Sign-out means revoke.

What the buyer can do#

JobEndpoints
ProfileGET /v1/me, PATCH /v1/me
Change emailPOST /v1/me/email-change-requests, then /confirm
Orders/v1/me/orders, /v1/me/orders/{order_id}, /activities, /receipt
Money/v1/me/payments, /v1/me/refunds
Delivery/v1/me/fulfillments, /v1/me/shipments, /v1/me/packages
Invoices/v1/me/invoices, /{invoice_id}, /pdf, /checkout-session
Subscriptions/v1/me/subscriptions, plus /pause, /resume, /cancel, /reactivate, /payment-method
Saved cards/v1/me/payment-methods, plus /set-default and delete
Addresses/v1/me/addresses, plus /set-default
Returns/return-eligibility-checks, /returns, /cancel, /return-resolution-previews, /resolutions, /checkout-session
Account closure/v1/me/deletion-requests

Each one behaves like its merchant-key counterpart, so the Returns, invoicing, and subscription billing guides all apply. Drop the customer_id and swap the credential.

How saved address defaults work#

Use /v1/me/addresses for buyer-owned address changes. The first saved address becomes both defaults. Setting or editing a saved default also makes that postal address the customer's effective billing or shipping address, including for future subscription tax calculations.

When a linked customer starts a Flint-hosted checkout, Flint prefills any missing billing or shipping address from those effective defaults. An address supplied in prefilled_customer_info remains authoritative for that checkout.

Merchant integrations can still write billing_address and shipping_address through /v1/customers/{customer_id}. Writing one of those compatibility fields removes the corresponding default designation from the address book. That field remains effective until the buyer or merchant selects another saved default.

PATCH /v1/me accepts name and phone. Send address changes to /v1/me/addresses so the address book and effective customer values change together.

customer_id is rejected, not ignored#

Sending a customer_id in the query string, or nested anywhere in the request body, fails with ME_CUSTOMER_ID_FORBIDDEN.

Rejecting is deliberate. Silently ignoring a customer_id would let a caller believe they had scoped a request when they had not, and that belief is how impersonation bugs ship.

A resource that exists but belongs to a different buyer returns 404, not 403. The namespace will not confirm that someone else's order ID is real.

expand is not available on /v1/me.

The Node SDK has no generated services for customer sessions or /v1/me yet, so this surface is plain HTTP today even if the rest of your integration uses the SDK.

A narrower view, on purpose#

/v1/me is not the merchant surface with a filter bolted on. Reads are trimmed to what a buyer should see, and writes to what a buyer should be able to do.

Returns show this most clearly. Read through a customer session, a Return carries a shorter supported_actions and a completion_blockers list filtered to the four a buyer can personally clear. The rest of that list is warehouse and merchant work, and showing it to a buyer reads as "your return is stuck" when nothing is wrong. See buyer-initiated returns.

If your account UI needs something the buyer view does not carry, fetch it on your backend with your merchant key and decide for yourself whether the buyer should see it. Do not reach for a wider credential in the buyer's request path.

Money still moves through Flint#

Paying an invoice and paying the difference on a Return resolution both go through a checkout session:

Bash
curl -X POST https://api.withflintpay.com/v1/me/invoices/inv_1kmn0aExample/checkout-session \
  -H "Authorization: Bearer flint_cses_example"

Redirect the buyer to the returned session. A customer session never carries payment authority, so an account you build cannot charge a card directly, in the same way Flint's hosted account cannot.

Let the buyer add a card#

Adding a card is the one flow where people assume they are blocked and are not. Flint is not callable from your browser code, but this flow never needs to be.

POST /v1/me/payment-methods returns the saved method plus everything Stripe.js needs:

JSON
{
  "data": {
    "payment_method": {
      "payment_method_id": "pm_1kmn0aExample",
      "status": "pending"
    },
    "client_setup": {
      "stripe": {
        "account_id": "acct_1kmn0aExample",
        "publishable_key": "pk_test_1kmn0aExample",
        "setup_intent": {
          "client_secret": "seti_1kmn0aExample_secret_...",
          "stripe_js_call": "confirm_setup"
        }
      }
    }
  }
}

Your backend makes that call under the customer session, then hands account_id, publishable_key, and the setup intent's client_secret to your own page. The page loads Stripe.js, mounts Elements, and performs the named stripe_js_call. Every browser request goes to Stripe, none to Flint, so the missing browser credential is irrelevant here.

Two things to build around:

  • The method starts pending. It becomes active when the processor confirms, which arrives as a webhook rather than a synchronous response. Do not render the card as usable, or set it as default, on the strength of the create call alone. Listen for the payment-method webhook and reconcile.
  • The publishable key is Flint's platform key, not one you own or rotate. Use the one in the response rather than hardcoding a key, because it tracks the active payment mode.

Removing a card and changing the default are plain calls: DELETE /v1/me/payment-methods/{payment_method_id} and POST .../set-default.

Point Flint's email at your pages#

Building the UI is half the job. The other half is making sure Flint's transactional email sends buyers to it instead of to account.withflintpay.com:

Bash
curl -X PATCH https://api.withflintpay.com/v1/settings \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_account": {
      "mode": "merchant_hosted",
      "merchant_account_url": "https://cedarandstone.com/account",
      "route_templates": {
        "order": "/orders/{resource_id}",
        "subscription": "/subscriptions/{resource_id}",
        "return": "/returns/{resource_id}"
      }
    }
  }'

Links resolve when the buyer clicks, so this also repoints mail you sent before you shipped the account. See customer accounts.

Note that Flint sends those emails from a link a buyer opens without a session. Your account pages must handle an unauthenticated arrival by logging the buyer in and then returning them to the link they clicked.

Next steps#

Rate this doc