Skip to main content
Flynet Payment Intents let approved partners accept payments from Blackbird members in FLY, on behalf of third-party merchants. The model is closely analogous to Stripe Payment Intents: you create an intent, the member confirms it, and FLY transfers from the member’s wallet to the merchant.

The merchant ID is your third credential

Every POST /payment_intents requires a flynet_merchant_id, a UUID identifying the merchant receiving funds. You receive it in your onboarding email if you applied for payments access. It’s scoped to your client_id and your environment; staging and production merchant IDs differ. If flynet_merchant_id is missing, wrong, or scoped to a different partner, create returns:
{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_not_found",
    "message": "Flynet merchant not found.",
    "param": null
  }
}
The merchant ID is not a secret on its own (it’s an addressing field), but it’s how the API knows which payee to route to. Store it alongside client_id and API_KEY in your env config; don’t hard-code it.

The primitive

A PaymentIntent moves FLY from a member’s wallet to a merchant’s wallet. Each intent has:
  • A flynet_merchant_id - the merchant receiving the payment
  • A customer_user_id - the Blackbird member paying
  • An amount in FLY
  • A status that derives from event timestamps

Status lifecycle

                  confirm
pending -----------------------> paid
   |                              |
   | cancel                       | refund
   v                              v
canceled                       refunded

(expires_at elapses before confirm -> expired)
StatusWhen
pendingIntent created, not yet confirmed
paidConfirmed; FLY has transferred
canceledCanceled before confirm
refundedPaid then refunded; FLY returned to member
expiredexpires_at elapsed without confirm
Tasting note - status is computed at read time from the timestamps on the intent. You do not transition states directly; you call cancel, confirm, or refund and the status follows.

v1 capabilities

Capabilityv1 status
FLY-to-FLY transfersLive
Idempotent createLive
Cancel pending intentsLive
Refund paid intents, full onlyLive
Receipt emails on confirmLive
Card-funded paymentsComing soon
Partial refundsComing soon
Webhook deliveryComing soon; poll status for v1
Hosted checkout pageComing soon
Chef’s warning - v1 is FLY-only. If the member does not already hold enough FLY, confirm returns 400 payment0030. You cannot card-fund or auto-load FLY in v1.

Idempotency

Network calls fail. Retries are normal. Without idempotency, retries on POST /payment_intents could create duplicate charges. Flynet uses idempotency keys to make retries safe. Every Payment Intent create requires an idempotency_key in the request body. The key is unique per (flynet_merchant_id, idempotency_key).
ScenarioResponse
First create with key X201 Created, new intent
Replay with same key X200 OK, same intent returned
New create with key Y201 Created, new intent
async function createPaymentIntent(orderId) {
  const response = await fetch(`${API_BASE_URL}/payment_intents`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${ACCESS_TOKEN}`,
    },
    body: JSON.stringify({
      flynet_merchant_id: process.env.MERCHANT_ID,
      customer_user_id: userId,
      amount: { value: "1000000000000000000", currency: "FLY" },
      idempotency_key: orderId,
      description: `Order ${orderId}`,
    }),
  });
  return response.json();
}
From the kitchen - Use your own order ID as the idempotency key. Network retries on the same order naturally return the same Payment Intent without creating a duplicate.
Idempotency on confirm, cancel, and refund is state-based: re-confirming a paid intent or re-canceling a canceled intent returns the same intent without side effects. No separate key needed.

Money

All amounts use the Money wire shape: { value, currency } with value as a stringified integer in FLY wei.

Receipts

On successful confirm, the customer receives a receipt email from flynet@blackbird.xyz.

Auth

All Payment Intent routes require an OAuth access token. API keys are not accepted on payment routes.

Endpoints at a glance

MethodPathPurpose
POST/payment_intentsCreate
GET/payment_intents/{id}Retrieve
GET/payment_intentsList
POST/payment_intents/{id}/cancelCancel pending
POST/payment_intents/{id}/confirmConfirm and transfer FLY
POST/payment_intents/{id}/refundRefund a paid intent
See the API Reference and the first payment recipe.

What’s coming

Card-funded confirm, partner webhooks, partial refunds, and a hosted checkout surface are on the roadmap.