View as Markdown

llms.txt

No such blueprint

This instance publishes no blueprint at that address. The catalog lists every one it does publish.

Back to the blueprints

Blueprints Full integrations

Invoice a customer and get paid

Create a customer, write them an invoice, issue it, and record the payment when it arrives, so you know what the platform does at each stage of an invoice's life.

Signed in, you can run this blueprint against your own sandbox merchant one call at a time, with the values from each call threaded into the next. Sign in to run it.

9 steps, 6 API callsInvoicingCustomersPayments

Samples use {{API_KEY}} for your API key and {{BASE_URL}} for this instance's API address. Anything else in double braces is a value an earlier step gave you.

Set up the customer

1. Read your merchant id off a sandbox sale

API call

POST /api/transactions

Every invoicing call names the merchant it bills as, and your key belongs to exactly one. There is no call that answers with that id on its own, so read it off the first merchant-scoped record your key creates: here, a card sale at the amount the sandbox always approves. If you have already run another flow on this site, the merchantId on any of its responses is the same value.

Reference for this operation

Values this step gives you

  • {{merchantId}} The merchant the sale belongs to, from the response body's merchantId property. Every invoicing call below bills as this merchant.
cURL
curl -X POST "{{BASE_URL}}/api/transactions" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "transactionType": "Sale",
    "cardData": {
      "cardNumber": "4111111111111111",
      "nameOnCard": "Jane Doe",
      "expirationMonth": 12,
      "expirationYear": 2030,
      "cvv": 123
    },
    "invoiceData": {
      "amounts": { "base": 10.00, "total": 10.00 }
    }
  }'
.NET
using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };
http.DefaultRequestHeaders.Add("api-key", "{{API_KEY}}");

var response = await http.PostAsJsonAsync("/api/transactions", new
{
    transactionType = "Sale",
    cardData = new
    {
        cardNumber = "4111111111111111",
        nameOnCard = "Jane Doe",
        expirationMonth = 12,
        expirationYear = 2030,
        cvv = 123
    },
    invoiceData = new
    {
        amounts = new { @base = 10.00m, total = 10.00m }
    }
});

response.EnsureSuccessStatusCode();

var sale = await response.Content.ReadFromJsonAsync<JsonElement>();
var merchantId = sale.GetProperty("merchantId").GetString();

What this step answers with

Abridged to the properties this step depends on. A real response carries more.

HTTP 200
{
  "id": "9f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "merchantId": "3a7b1c9d-2e4f-4a6b-8c8d-9e0f1a2b3c4d",
  "transactionType": "Sale",
  "resultCode": "Ok",
  "authorizedAmount": 10.00
}

2. Create the customer you are invoicing

API call

POST /api/customers

An invoice is addressed to a customer record, so the customer comes first. Give the address list exactly one default entry with a street line, a city, a state and a postal code; the list may be left empty, but a partial address is refused. Keep the id: it's the recipient of the invoice below, and it's also how a customer portal or a payment link knows whose invoice it's showing.

Reference for this operation

Values this step gives you

  • {{customerId}} The id of the created customer, from the response body's id property. The invoice names it as its recipient.
cURL
curl -X POST "{{BASE_URL}}/api/customers" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "merchantId": "{{merchantId}}",
    "name": "Jane Doe",
    "isActive": true,
    "contactDetail": {
      "primaryContactName": "Jane Doe",
      "emailAddress": "jane.doe@example.com",
      "phone": "5555550123",
      "addresses": [
        {
          "address1": "100 Main Street",
          "city": "Minneapolis",
          "state": "MN",
          "zip": "55401",
          "isDefault": true
        }
      ]
    }
  }'
.NET
var created = await http.PostAsJsonAsync("/api/customers", new
{
    merchantId,
    name = "Jane Doe",
    isActive = true,
    contactDetail = new
    {
        primaryContactName = "Jane Doe",
        emailAddress = "jane.doe@example.com",
        phone = "5555550123",
        addresses = new[]
        {
            new
            {
                address1 = "100 Main Street",
                city = "Minneapolis",
                state = "MN",
                zip = "55401",
                isDefault = true
            }
        }
    }
});

created.EnsureSuccessStatusCode();

var customer = await created.Content.ReadFromJsonAsync<JsonElement>();
var customerId = customer.GetProperty("id").GetString();

What this step answers with

Abridged to the properties this step depends on. A real response carries more.

HTTP 200
{
  "id": "7b2c4d6e-8f0a-4c3d-9e5f-6a7b8c9d0e1f",
  "merchantId": "{{merchantId}}",
  "name": "Jane Doe",
  "isActive": true
}

Write the invoice

3. Create the invoice as a draft

API call

POST /api/invoicing/invoices

Send the biller, the recipient, the currency, the terms and the lines. The platform answers with a draft: totals are computed, the status is Draft, and there is no invoice number yet. A draft is the one stage you can still edit, so this is where your own review or approval step belongs. Lines need only a description, a quantity and a unit price; link one to a catalog product when you sell the same thing again and again, and leave productId off when you don't.

Reference for this operation

Values this step gives you

  • {{invoiceId}} The id of the draft, from the response body's id property. Every later call addresses the invoice by it; the invoice number is for people and isn't assigned until issue.
cURL
curl -X POST "{{BASE_URL}}/api/invoicing/invoices" \
  -H "api-key: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -d '{
    "billerId": "{{merchantId}}",
    "billerType": "Merchant",
    "recipientId": "{{customerId}}",
    "recipientType": "Customer",
    "currency": "USD",
    "paymentTermsValue": "Net30",
    "recipientSnapshot": {
      "name": "Jane Doe",
      "email": "jane.doe@example.com"
    },
    "lineItems": [
      { "description": "Website redesign", "quantity": 1, "unitPrice": 1200.00 },
      { "description": "Managed hosting, monthly", "quantity": 12, "unitPrice": 25.00 }
    ]
  }'
.NET
var drafted = await http.PostAsJsonAsync("/api/invoicing/invoices", new
{
    billerId = merchantId,
    billerType = "Merchant",
    recipientId = customerId,
    recipientType = "Customer",
    currency = "USD",
    paymentTermsValue = "Net30",
    recipientSnapshot = new { name = "Jane Doe", email = "jane.doe@example.com" },
    lineItems = new[]
    {
        new { description = "Website redesign", quantity = 1m, unitPrice = 1200.00m },
        new { description = "Managed hosting, monthly", quantity = 12m, unitPrice = 25.00m }
    }
});

drafted.EnsureSuccessStatusCode();

var draft = await drafted.Content.ReadFromJsonAsync<JsonElement>();
var invoiceId = draft.GetProperty("id").GetString();
var status = draft.GetProperty("status").GetString(); // "Draft"

What this step answers with

Abridged to the properties this step depends on. A real response carries more.

HTTP 200
{
  "id": "8c0d2e4f-6a7b-4c9d-8e1f-2a3b4c5d6e7f",
  "billerId": "{{merchantId}}",
  "billerType": "Merchant",
  "recipientId": "{{customerId}}",
  "recipientType": "Customer",
  "status": "Draft",
  "invoiceNumber": null,
  "currency": "USD",
  "paymentTermsValue": "Net30",
  "issueDate": null,
  "dueDate": null,
  "subtotal": 1500.00,
  "taxTotal": 0.00,
  "grandTotal": 1500.00,
  "amountPaid": 0.00,
  "balanceDue": 1500.00,
  "paidAt": null,
  "isLocked": false,
  "lineItems": [
    {
      "description": "Website redesign",
      "quantity": 1,
      "unitPrice": 1200.00,
      "lineTotal": 1200.00
    },
    {
      "description": "Managed hosting, monthly",
      "quantity": 12,
      "unitPrice": 25.00,
      "lineTotal": 300.00
    }
  ]
}

4. Issue the invoice

API call

POST /api/invoicing/invoices/{{invoiceId}}/issue

Issuing is the point of no return. The platform assigns the invoice number from the biller's sequence, stamps the issue date, computes the due date from the Net 30 terms, freezes the biller and recipient snapshots, and locks the document: the status is Issued and an update from here on is refused. The body is empty; the invoice id in the route is the whole request. Do this when the invoice is final, and not before.

Reference for this operation

Values this step gives you

    cURL
    curl -X POST "{{BASE_URL}}/api/invoicing/invoices/{{invoiceId}}/issue" \
      -H "api-key: {{API_KEY}}"
    .NET
    var issuing = await http.PostAsync(
        $"/api/invoicing/invoices/{invoiceId}/issue", content: null);
    
    issuing.EnsureSuccessStatusCode();
    
    var issued = await issuing.Content.ReadFromJsonAsync<JsonElement>();
    var invoiceNumber = issued.GetProperty("invoiceNumber").GetString();
    var balanceDue = issued.GetProperty("balanceDue").GetDecimal();

    What this step answers with

    Abridged to the properties this step depends on. A real response carries more.

    HTTP 200
    {
      "id": "{{invoiceId}}",
      "billerId": "{{merchantId}}",
      "billerType": "Merchant",
      "recipientId": "{{customerId}}",
      "recipientType": "Customer",
      "status": "Issued",
      "invoiceNumber": "INV-00001",
      "currency": "USD",
      "paymentTermsValue": "Net30",
      "issueDate": "2026-02-04T18:22:43.062Z",
      "dueDate": "2026-03-06T18:22:43.062Z",
      "subtotal": 1500.00,
      "taxTotal": 0.00,
      "grandTotal": 1500.00,
      "amountPaid": 0.00,
      "balanceDue": 1500.00,
      "paidAt": null,
      "isLocked": true,
      "lineItems": [
        {
          "description": "Website redesign",
          "quantity": 1,
          "unitPrice": 1200.00,
          "lineTotal": 1200.00
        },
        {
          "description": "Managed hosting, monthly",
          "quantity": 12,
          "unitPrice": 25.00,
          "lineTotal": 300.00
        }
      ]
    }

    Get paid

    5. Record the payment

    API call

    POST /api/invoicing/invoices/{{invoiceId}}/record-payment

    When the customer pays outside the platform, by check or by bank transfer, tell the invoice about it. Send the amount received and how it arrived. The platform applies it to the balance and moves the status: to Paid when the balance reaches zero, as here, or to PartiallyPaid when it doesn't. A partial amount is refused unless the invoice allows partial payment, and any amount above the balance is refused outright. A payment the customer makes through a payment link is applied for you by the same rule, so your reconciliation reads one status whichever way the money came.

    Reference for this operation

    Values this step gives you

      cURL
      curl -X POST "{{BASE_URL}}/api/invoicing/invoices/{{invoiceId}}/record-payment" \
        -H "api-key: {{API_KEY}}" \
        -H "Content-Type: application/json" \
        -d '{
          "amount": 1500.00,
          "paymentMethod": "Check",
          "notes": "Check received by mail."
        }'
      .NET
      var recording = await http.PostAsJsonAsync(
          $"/api/invoicing/invoices/{invoiceId}/record-payment",
          new
          {
              amount = 1500.00m,
              paymentMethod = "Check",
              notes = "Check received by mail."
          });
      
      recording.EnsureSuccessStatusCode();
      
      var paid = await recording.Content.ReadFromJsonAsync<JsonElement>();
      var paidStatus = paid.GetProperty("status").GetString(); // "Paid"

      What this step answers with

      Abridged to the properties this step depends on. A real response carries more.

      HTTP 200
      {
        "id": "{{invoiceId}}",
        "billerId": "{{merchantId}}",
        "billerType": "Merchant",
        "recipientId": "{{customerId}}",
        "recipientType": "Customer",
        "status": "Paid",
        "invoiceNumber": "INV-00001",
        "currency": "USD",
        "paymentTermsValue": "Net30",
        "issueDate": "2026-02-04T18:22:43.062Z",
        "dueDate": "2026-03-06T18:22:43.062Z",
        "subtotal": 1500.00,
        "taxTotal": 0.00,
        "grandTotal": 1500.00,
        "amountPaid": 1500.00,
        "balanceDue": 0.00,
        "paidAt": "2026-02-04T18:22:43.062Z",
        "isLocked": true,
        "lineItems": [
          {
            "description": "Website redesign",
            "quantity": 1,
            "unitPrice": 1200.00,
            "lineTotal": 1200.00
          },
          {
            "description": "Managed hosting, monthly",
            "quantity": 12,
            "unitPrice": 25.00,
            "lineTotal": 300.00
          }
        ]
      }

      6. Read the invoice back

      API call

      GET /api/invoicing/invoices/{{invoiceId}}

      Read the invoice you just settled. The status is Paid, the balance due is zero, the amount paid equals the grand total, and paidAt records when the balance cleared. Reconcile against this record rather than against the response you got from recording the payment, so a request that timed out on your side still has somewhere to recover the outcome from.

      Reference for this operation

      Values this step gives you

        cURL
        curl "{{BASE_URL}}/api/invoicing/invoices/{{invoiceId}}" \
          -H "api-key: {{API_KEY}}"
        .NET
        var invoice = await http.GetFromJsonAsync<JsonElement>(
            $"/api/invoicing/invoices/{invoiceId}");
        
        var settled = invoice.GetProperty("status").GetString() == "Paid"
                      && invoice.GetProperty("balanceDue").GetDecimal() == 0m;
        var paidAt = invoice.GetProperty("paidAt").GetString();

        What this step answers with

        Abridged to the properties this step depends on. A real response carries more.

        HTTP 200
        {
          "id": "{{invoiceId}}",
          "billerId": "{{merchantId}}",
          "billerType": "Merchant",
          "recipientId": "{{customerId}}",
          "recipientType": "Customer",
          "status": "Paid",
          "invoiceNumber": "INV-00001",
          "currency": "USD",
          "paymentTermsValue": "Net30",
          "issueDate": "2026-02-04T18:22:43.062Z",
          "dueDate": "2026-03-06T18:22:43.062Z",
          "subtotal": 1500.00,
          "taxTotal": 0.00,
          "grandTotal": 1500.00,
          "amountPaid": 1500.00,
          "balanceDue": 0.00,
          "paidAt": "2026-02-04T18:22:43.062Z",
          "isLocked": true,
          "lineItems": [
            {
              "description": "Website redesign",
              "quantity": 1,
              "unitPrice": 1200.00,
              "lineTotal": 1200.00
            },
            {
              "description": "Managed hosting, monthly",
              "quantity": 12,
              "unitPrice": 25.00,
              "lineTotal": 300.00
            }
          ]
        }

        Go further

        7. Send the invoice to the customer

        On your side

        In production you send the invoice rather than recording a payment by hand. The send call runs the whole delivery workflow: it issues the invoice if it's still a draft, creates a payment link and a view token for the recipient, marks the status Sent, and delivers the notification. That last part needs a merchant with a configured email destination, which a fresh sandbox may not have, and it's why this flow records a payment as its runnable path instead. Once you have configured delivery, call send from your own integration and let the payment link do the collecting.

        Reference for this operation

        Values this step gives you

          8. Download the invoice as a PDF

          On your side

          Any issued invoice can be rendered as a PDF from the snapshots and lines frozen at issue. Fetch it when your customer asks for a copy or when your own records need the document rather than the data. The response is the file itself, not JSON.

          Reference for this operation

          Values this step gives you

            9. Offer a payment plan

            On your side

            A larger invoice can be split into scheduled installments the platform collects against a stored payment method. Create the plan on an issued invoice; each installment is applied to the balance the same way the payment above was, so the invoice reaches Paid when the last one clears. Payment plans, credit notes and recurring invoices each have their own reference pages.

            Reference for this operation

            Values this step gives you

              Reconnecting to the server

              Could not reconnect

              This session has ended

              Attempt 1

              Your work on this page is still here. Retrying keeps it; reloading starts the page again.

              The server no longer holds this page's state, so it has to be loaded again.