Skip to content

Record sales and receive payments

A sale that comes in through the API goes down the same path as the sales screen: it gets the company’s number, counts against the plan quota, builds the installments, posts to finance and deducts stock for catalog products. This guide registers a sale in installments and then settles one installment.

PermissionWhat for
sales:createRegister the sale
sales:updateReceive, reverse, reschedule, cancel and create a new version
sales:readRead the sale later
finance:createOnly when the call asks for the finance posting: track_in_finance: true or an account in payment_plan.account_id
finance:readTo choose the account in payment_plan.account_id, and to see the sale’s account and transactions in the response

Receiving a payment needs only the sales permission, even when the sale is in finance: the transaction is a consequence of the sale, as on screen.

The member’s access to finance counts too. Sending the track_in_finance field, with true or with false, and choosing the account in payment_plan.account_id require the member the key acts as to have access to finance in the dashboard; without it, the response is 403 member_permission_denied.

Every sale carries a payment_plan, and it always has all six fields, even when some are null:

FieldWhat it is
modepaid_full, entry_and_rest, unpaid or installments
payment_methodpix, credit_card, debit_card, cash, boleto, transfer, other or null
account_idThe receiving account, or null for the company’s default account
paid_atDate of the upfront payment or of the down payment (YYYY-MM-DD), or null
entry_amount_centsDown payment amount, in entry_and_rest mode; null in the others
installmentsThe installments, each with amount_cents and due_date; null in paid_full

The four modes are the ones on screen:

  • paid_full: paid upfront. The sale starts paid.
  • entry_and_rest: a down payment now and the rest in installments. The down payment must be greater than zero and less than the total, and the installments add up to what is left.
  • installments: in installments, nothing paid yet. The installments add up to the total.
  • unpaid: on credit, to be received. It requires client_id, and the installments add up to the total.

The installments must add up to the amount. A difference of up to 1 cent is absorbed by the last installment; more than that returns 422. The total is calculated from the items, the discount, the shipping and the additional costs, the way the form calculates it; you do not send the total.

Register the sale and receive the first installment

Section titled “Register the sale and receive the first installment”
Terminal window
# 1. A R$ 1,500.00 sale in 3 boleto installments
# Generate the key once and keep it: on a retry, repeat with the same value.
SALE_KEY=$(uuidgen)
curl -X POST "https://api.fatureihoje.com/public/v1/sales" \
-H "Authorization: Bearer $FH_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $SALE_KEY" \
-d '{
"client_id": "0d9a7c5e-3f1b-4d2a-8c6e-4a2b0c8e6f1d",
"items": [
{ "type": "custom", "name": "Installation of 4 cameras", "quantity": 1, "unit_price_cents": 150000 }
],
"payment_plan": {
"mode": "installments",
"payment_method": "boleto",
"account_id": null,
"paid_at": null,
"entry_amount_cents": null,
"installments": [
{ "amount_cents": 50000, "due_date": "2026-10-10" },
{ "amount_cents": 50000, "due_date": "2026-11-10" },
{ "amount_cents": 50000, "due_date": "2026-12-10" }
]
}
}'
# 2. The first installment was paid. Use the sale id and the installment id
# that came back in the response above.
SALE_ID="3c5e7a9b-1d3f-4b5d-8f1a-3c5e7a9b1d3f"
INSTALLMENT_ID="inst-1"
# Generate the key once and keep it: on a retry, repeat with the same value.
PAYMENT_KEY=$(uuidgen)
curl -X POST "https://api.fatureihoje.com/public/v1/sales/$SALE_ID/payments" \
-H "Authorization: Bearer $FH_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $PAYMENT_KEY" \
-d "{
\"amount_cents\": 50000,
\"paid_at\": \"2026-10-09\",
\"payment_method\": \"pix\",
\"installment_id\": \"$INSTALLMENT_ID\"
}"

The sale, item and response fields are in the Reference. Three points from the example:

  • A custom item is ad hoc and carries the name you send. A catalog item points to a registered product (product_id, plus variation_id when the product has variations), and then the name and the promotional price come from the product.
  • The sale responds with installments, each one with its own id. That id goes in installment_id when receiving, and in rescheduling.
  • payment_status moves from pending to partial and then paid as payments come in, and paid_cents adds up what has been received.

POST /public/v1/sales/{id}/payments is the Receive dialog on screen:

  • paid_at is the date the money came in (YYYY-MM-DD), and it is required.
  • The amount cannot exceed the sale’s balance.
  • With installment_id, the amount settles that installment, which must be open. Without it, the amount is applied in due-date order.
  • account_id chooses the account that received the money, and needs finance:read and finance:create on the key, plus the member’s access to finance. Without it, the sale’s account is used.

Every write carries an Idempotency-Key, and for payments it matters more. A payment retried after a network failure, without the key, goes in twice. With the same key, the retry returns the first response and nothing is written again. See Idempotency.

A wrong payment is reversed with POST /public/v1/sales/{id}/payments/{payment_id}/cancel, with the reason in reason. To change amounts and due dates of open installments, use PATCH /public/v1/sales/{id}/installments, which is the Reschedule dialog on screen.

  • Finance. Without the track_in_finance field, the company’s setting applies, and that automatic posting needs no finance permission. Sending the field, with any value, requires the member to have access to finance; true also requires finance:create on the key, and false does not. Choosing the account in payment_plan.account_id requires finance:read and finance:create on the key, plus the member’s access.
  • What you see. account_id and finance_transactions only come in the response when the key has finance:read and its member has access to finance. Without that, the sale comes without those two fields, as in the dashboard. Installments and payments always come.
  • Stock. decrement_stock defaults to true, as on screen, and deducts stock for catalog items without needing products:update. Send false when stock is managed somewhere else.

A sale is not deleted, not even in the dashboard.

  • A commercial change (items, amounts, client, seller, balance plan) is POST /public/v1/sales/{id}/versions, with the reason in reason. The sale id does not change, and version goes up. What was already received still counts. GET /public/v1/sales/{id}/versions returns the history.
  • Notes and production change with a PATCH /public/v1/sales/{id}, without a new version.
  • Canceling is POST /public/v1/sales/{id}/cancel, with the reason. It reverses the payments, returns the deducted stock and cancels the transactions.

The locks are the ones on screen, action by action: a sale that is canceled, has an active invoice, has its commission already paid or was generated by a contract refuses some actions with 409. The full table is in the Overview.

ResponseWhat to do
422 validation_failedFix the field listed in errors, such as installments that do not add up to the total
409 conflictThe sale’s state does not allow it: canceled, active invoice, paid commission or contract
403 permission_missingThe key is missing a permission, such as finance:create with track_in_finance: true
403 member_permission_deniedThe key’s member has no access to finance and the call touches finance
429 rate_limit_exceededWait for Retry-After and retry. See Rate limits
5xx or network failureRetry with the same Idempotency-Key

For your system to know the sale was paid, including when the payment was entered in the dashboard, subscribe an endpoint to sale.payment_registered and sale.paid. Sale events never carry the account or the transactions; those have their own financial_transaction.* events. See the Event catalog.