Docs
API 2026-05-27.dahlia Dashboard

Get started

Go from a test secret key to your first successful test payment in a few minutes, with plain HTTP.

You make one test payment with Fluveo's /v1 API. You need a terminal with curl (or Node.js, Python or PHP) and a Fluveo account. Nothing on this page moves real money: every call uses a test key.

  1. Get a test secret key#

    In the Fluveo Dashboard, open Settings in the sidebar. In the API keys section, click Secret key. The bottom of the sidebar shows Test mode, so the key is a test key and starts with sk_test_. The Dashboard shows the full key once, so copy it to a safe place.

    Keep the key on your server. Never put it in a browser, a mobile app or a commit.

  2. Check that the key works#

    Read your account. The answer's id is your store's id, which starts with mch_. Every object you create belongs to that store.

    Read your account
    curl https://api.fluveo.com/v1/account \
      -H "Authorization: Bearer sk_test_..."
    const response = await fetch("https://api.fluveo.com/v1/account", {
      headers: { Authorization: "Bearer sk_test_..." },
    });
    console.log(await response.json());
    import requests
    
    response = requests.get(
        "https://api.fluveo.com/v1/account",
        headers={"Authorization": "Bearer sk_test_..."},
    )
    print(response.json())
    <?php
    $ch = curl_init("https://api.fluveo.com/v1/account");
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ["Authorization: Bearer sk_test_..."],
    ]);
    echo curl_exec($ch);

    A 401 means the key is missing or wrong. See Authentication.

  3. Pay with a test card#

    Create a PaymentIntent and confirm it in one request. An amount is a whole number in the currency's smallest unit, so 2000 is $20.00. pm_card_visa is a test card that the bank always approves.

    Pay $20.00 with a test card
    curl https://api.fluveo.com/v1/payment_intents \
      -H "Authorization: Bearer sk_test_..." \
      -H "Idempotency-Key: quickstart-order-1001" \
      -d amount=2000 \
      -d currency=usd \
      -d "payment_method_types[]=card" \
      -d payment_method=pm_card_visa \
      -d confirm=true
    const response = await fetch("https://api.fluveo.com/v1/payment_intents", {
      method: "POST",
      headers: {
        Authorization: "Bearer sk_test_...",
        "Idempotency-Key": "quickstart-order-1001",
      },
      body: new URLSearchParams({
        amount: "2000",
        currency: "usd",
        "payment_method_types[]": "card",
        payment_method: "pm_card_visa",
        confirm: "true",
      }),
    });
    const paymentIntent = await response.json();
    console.log(paymentIntent.id, paymentIntent.status);
    import requests
    
    response = requests.post(
        "https://api.fluveo.com/v1/payment_intents",
        headers={
            "Authorization": "Bearer sk_test_...",
            "Idempotency-Key": "quickstart-order-1001",
        },
        data={
            "amount": 2000,
            "currency": "usd",
            "payment_method_types[]": "card",
            "payment_method": "pm_card_visa",
            "confirm": "true",
        },
    )
    payment_intent = response.json()
    print(payment_intent["id"], payment_intent["status"])
    <?php
    $ch = curl_init("https://api.fluveo.com/v1/payment_intents");
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            "Authorization: Bearer sk_test_...",
            "Idempotency-Key: quickstart-order-1001",
        ],
        CURLOPT_POSTFIELDS => http_build_query([
            "amount" => 2000,
            "currency" => "usd",
            "payment_method_types" => ["card"],
            "payment_method" => "pm_card_visa",
            "confirm" => "true",
        ]),
    ]);
    $paymentIntent = json_decode(curl_exec($ch), true);
    echo $paymentIntent["id"] . " " . $paymentIntent["status"];

    The PaymentIntent comes back succeeded, and latest_charge names the Charge it made (ch_...). That is your first test payment.

    Response (shortened)
    {
      "id": "pi_...",
      "object": "payment_intent",
      "amount": 2000,
      "currency": "usd",
      "status": "succeeded",
      "latest_charge": "ch_...",
      "livemode": false
    }
  4. Ask for a Pix#

    The same call takes a Pix. Name the method in payment_method_data[type] and the payer in payment_method_data[billing_details]: name, email, phone and CPF. 1000 is R$ 10,00, and 12345678909 is a CPF for tests. The currency each method charges in is on Payments.

    Create a R$ 10,00 Pix charge
    curl https://api.fluveo.com/v1/payment_intents \
      -H "Authorization: Bearer sk_test_..." \
      -H "Idempotency-Key: quickstart-pedido-1001" \
      -d amount=1000 \
      -d currency=brl \
      -d "payment_method_types[]=pix" \
      -d confirm=true \
      -d "payment_method_data[type]=pix" \
      --data-urlencode "payment_method_data[billing_details][name]=Ada Lovelace" \
      -d "payment_method_data[billing_details][email]=ada@example.com" \
      --data-urlencode "payment_method_data[billing_details][phone]=+5511999999999" \
      -d "payment_method_data[billing_details][tax_id]=12345678909"
    const response = await fetch("https://api.fluveo.com/v1/payment_intents", {
      method: "POST",
      headers: {
        Authorization: "Bearer sk_test_...",
        "Idempotency-Key": "quickstart-pedido-1001",
      },
      body: new URLSearchParams({
        amount: "1000",
        currency: "brl",
        "payment_method_types[]": "pix",
        confirm: "true",
        "payment_method_data[type]": "pix",
        "payment_method_data[billing_details][name]": "Ada Lovelace",
        "payment_method_data[billing_details][email]": "ada@example.com",
        "payment_method_data[billing_details][phone]": "+5511999999999",
        "payment_method_data[billing_details][tax_id]": "12345678909",
      }),
    });
    const paymentIntent = await response.json();
    console.log(paymentIntent.status, paymentIntent.next_action.pix_display_qr_code.data);
    import requests
    
    response = requests.post(
        "https://api.fluveo.com/v1/payment_intents",
        headers={
            "Authorization": "Bearer sk_test_...",
            "Idempotency-Key": "quickstart-pedido-1001",
        },
        data={
            "amount": 1000,
            "currency": "brl",
            "payment_method_types[]": "pix",
            "confirm": "true",
            "payment_method_data[type]": "pix",
            "payment_method_data[billing_details][name]": "Ada Lovelace",
            "payment_method_data[billing_details][email]": "ada@example.com",
            "payment_method_data[billing_details][phone]": "+5511999999999",
            "payment_method_data[billing_details][tax_id]": "12345678909",
        },
    )
    payment_intent = response.json()
    print(payment_intent["status"], payment_intent["next_action"]["pix_display_qr_code"]["data"])
    <?php
    $ch = curl_init("https://api.fluveo.com/v1/payment_intents");
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            "Authorization: Bearer sk_test_...",
            "Idempotency-Key: quickstart-pedido-1001",
        ],
        CURLOPT_POSTFIELDS => http_build_query([
            "amount" => 1000,
            "currency" => "brl",
            "payment_method_types" => ["pix"],
            "confirm" => "true",
            "payment_method_data" => [
                "type" => "pix",
                "billing_details" => [
                    "name" => "Ada Lovelace",
                    "email" => "ada@example.com",
                    "phone" => "+5511999999999",
                    "tax_id" => "12345678909",
                ],
            ],
        ]),
    ]);
    $paymentIntent = json_decode(curl_exec($ch), true);
    echo $paymentIntent["status"] . "\n" . $paymentIntent["next_action"]["pix_display_qr_code"]["data"];

    This time the status is requires_action: the buyer still has to pay. next_action.pix_display_qr_code carries what you put on screen. data is the code the buyer pastes in their bank app, image_url_png and image_url_svg draw it as a QR code, and expires_at is the moment it stops working.

    Response (shortened)
    {
      "id": "pi_...",
      "object": "payment_intent",
      "amount": 1000,
      "currency": "brl",
      "status": "requires_action",
      "next_action": {
        "type": "pix_display_qr_code",
        "pix_display_qr_code": {
          "data": "00020101...",
          "expires_at": 1767225600,
          "hosted_instructions_url": "https://...",
          "image_url_png": "https://...",
          "image_url_svg": "https://..."
        }
      },
      "livemode": false
    }
  5. See what happened#

    The payment you just made created an event. List the newest ones.

    List recent events
    curl -G https://api.fluveo.com/v1/events \
      -H "Authorization: Bearer sk_test_..." \
      -d limit=3
    const response = await fetch("https://api.fluveo.com/v1/events?limit=3", {
      headers: { Authorization: "Bearer sk_test_..." },
    });
    const events = await response.json();
    for (const event of events.data) console.log(event.type, event.data.object.id);
    import requests
    
    response = requests.get(
        "https://api.fluveo.com/v1/events",
        headers={"Authorization": "Bearer sk_test_..."},
        params={"limit": 3},
    )
    for event in response.json()["data"]:
        print(event["type"], event["data"]["object"]["id"])
    <?php
    $ch = curl_init("https://api.fluveo.com/v1/events?limit=3");
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ["Authorization: Bearer sk_test_..."],
    ]);
    foreach (json_decode(curl_exec($ch), true)["data"] as $event) {
        echo $event["type"] . " " . $event["data"]["object"]["id"] . "\n";
    }

    You see payment_intent.succeeded for a card you just paid, or payment_intent.created for a Pix that waits for the buyer. In production you get the same events pushed to your server as webhooks.

Test it#

  • Send the same request again with the same Idempotency-Key. You get the same PaymentIntent back, and nothing is charged twice. See Idempotency.
  • Swap pm_card_visa for pm_card_visa_chargeDeclined. The request answers 402 with a card_error. See Testing for every test value.

Next#