DevelopersAPI reference

Tap to Pay API

REST endpoints for tap to pay in ServFlo: 8 operations with parameters, request and response fields, permissions and curl examples.

Base URL: https://app.servflo.ai/api. Every call needs Authorization: Bearer sfk_… (Authentication); {organizationId} ($ORGANIZATION_ID in the examples) is your business's id.

OperationEndpointRisk
Tap to Pay: whether this business can take cards in personGET /organizations/{organizationId}/tap-to-payread
Tap to Pay: start taking a card for an invoice (moves money)POST /organizations/{organizationId}/invoices/{invoiceId}/tap-to-paysensitive
Tap to Pay: start taking a quote deposit by card (moves money)POST /organizations/{organizationId}/quotes/{quoteId}/tap-to-paysensitive
Tap to Pay: cancel an unfinished attemptPOST /organizations/{organizationId}/payments/{paymentId}/tap-to-pay/cancelwrite
A card payment taken in person: where it standsGET /organizations/{organizationId}/payments/{paymentId}/in-personread
The customer's saved card for an invoice, and whether it can be chargedGET /organizations/{organizationId}/invoices/{invoiceId}/saved-cardread
Charge the customer's saved card for an invoice (moves money)POST /organizations/{organizationId}/invoices/{invoiceId}/charge-saved-cardsensitive
Text or email the customer a receipt for a card paymentPOST /organizations/{organizationId}/payments/{paymentId}/receiptsensitive

Tap to Pay: whether this business can take cards in person

GET /organizations/{organizationId}/tap-to-pay

Whether cards can be taken in person on a phone (Tap to Pay on iPhone and Android, through Stripe Terminal) and whether this caller may charge a customer's saved card. Read-only; makes nothing at Stripe. The technician app shows its Tap to Pay button only when available is true (and the phone supports it).

Permission: payments.read (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk read (OAuth scope servflo.read).

Permissionpayments.read
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolonlinePayments_tapToPay (toolset invoices)

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/tap-to-pay" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
availablebooleanThe business can take cards in person: Stripe connected, charges enabled, card payments active and a business address for the Terminal location.
reasonenum or nullWhy not: no Stripe key on the server, no account, charges not enabled yet, card payments paused, or no business address. One of NOT_CONFIGURED, NOT_CONNECTED, NOT_ENABLED, CARD_PAYMENTS_INACTIVE, NO_ADDRESS.
problemstring or nullWhat to fix, in words, when reason is NO_ADDRESS.
modeenum or nulltest: use Stripe's simulated reader and test cards. One of test, live.
merchantDisplayNamestringThe business name shown on the tap sheet.
locationReadybooleanThe Stripe Terminal location exists (made on first use).
tipsEnabledboolean
techChargeSavedCardsbooleanThe owner's switch "Let technicians charge saved cards" (default off).
canChargeSavedCardsbooleanThis caller may charge a customer's saved card (always for the office).

Tap to Pay: start taking a card for an invoice (moves money)

POST /organizations/{organizationId}/invoices/{invoiceId}/tap-to-pay

Money: starts a card payment the customer taps on the technician's phone. Writes a PENDING payment (processor STRIPE, channel TAP_TO_PAY, payment.started) and a card_present PaymentIntent on the business's Stripe account for amountCents (default the balance due; less is allowed, at least 100) plus tipCents when the business takes tips. The phone collects the card with the Stripe Terminal SDK; Stripe's webhook settles the payment and the invoice (payment.recorded). An earlier unfinished Tap to Pay on this invoice is cancelled. 409 when the business can't take cards, 400 when nothing is due. Technicians: their own jobs' invoices.

Permission: payments.record (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk sensitive (OAuth scope servflo.sensitive).

Permissionpayments.record
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolonlinePayments_tapToPayInvoice (toolset invoices)
ApprovalOver MCP, this tool asks for your OK first (in the AI app when it supports it, otherwise in ServFlo). See Approvals.

Parameters

NameInTypeRequiredDescription
invoiceIdpathstringYes

Request body

FieldTypeRequiredDescription
amountCentsintegerNoCents to take; defaults to the whole balance (an invoice) or the deposit asked for (a quote). At least 100 when less than what is due. 1–100000000.
tipCentsintegerNoTip on top, in cents; only when the business takes tips. 0–100000000.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices/{invoiceId}/tap-to-pay" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amountCents":12500,"tipCents":12500}'

Response

201: the answer's fields
FieldTypeDescription
paymentIdstringThe PENDING payment (poll GET /payments/{paymentId}/in-person).
paymentIdentifierstring
paymentIntentIdstringThe card_present PaymentIntent on the business's Stripe account.
clientSecretstringFor the phone's Stripe Terminal SDK only (retrievePaymentIntent); never show or log it.
amountCentsinteger
tipCentsinteger
currencystring
locationIdstringThe business's Stripe Terminal location (tml_…).

Tap to Pay: start taking a quote deposit by card (moves money)

POST /organizations/{organizationId}/quotes/{quoteId}/tap-to-pay

Money: starts a card deposit the customer taps on the phone, on an APPROVED (or CONVERTED, not yet invoiced) quote. Writes a PENDING deposit payment (channel TAP_TO_PAY) and a card_present PaymentIntent on the business's Stripe account for amountCents (default the deposit the approval asked for; deposits may add up to the approved total). Stripe's webhook settles it; the deposit moves onto the invoice when the quote is invoiced. Technicians: the quotes they sold.

Permission: payments.record (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk sensitive (OAuth scope servflo.sensitive).

Permissionpayments.record
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolonlinePayments_tapToPayDeposit (toolset invoices)
ApprovalOver MCP, this tool asks for your OK first (in the AI app when it supports it, otherwise in ServFlo). See Approvals.

Parameters

NameInTypeRequiredDescription
quoteIdpathstringYes

Request body

FieldTypeRequiredDescription
amountCentsintegerNoCents to take; defaults to the whole balance (an invoice) or the deposit asked for (a quote). At least 100 when less than what is due. 1–100000000.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/quotes/{quoteId}/tap-to-pay" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amountCents":12500}'

Response

201: the answer's fields
FieldTypeDescription
paymentIdstringThe PENDING payment (poll GET /payments/{paymentId}/in-person).
paymentIdentifierstring
paymentIntentIdstringThe card_present PaymentIntent on the business's Stripe account.
clientSecretstringFor the phone's Stripe Terminal SDK only (retrievePaymentIntent); never show or log it.
amountCentsinteger
tipCentsinteger
currencystring
locationIdstringThe business's Stripe Terminal location (tml_…).

Tap to Pay: cancel an unfinished attempt

POST /organizations/{organizationId}/payments/{paymentId}/tap-to-pay/cancel

The customer didn't tap, or the person stopped: cancels the PaymentIntent at Stripe and marks the PENDING Tap to Pay payment FAILED (payment.failed, reason tap_cancelled). Takes no money and refunds nothing; a payment Stripe already took is left for its webhook to settle. Technicians: payments they started.

Permission: payments.record (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk write (OAuth scope servflo.write).

Permissionpayments.record
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolonlinePayments_cancelTapToPay (toolset invoices)

Parameters

NameInTypeRequiredDescription
paymentIdpathstringYes

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payments/{paymentId}/tap-to-pay/cancel" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
idstring
identifierstringPAY-n.
statusenumPENDING until Stripe's webhook settles a Tap to Pay payment; SUCCEEDED counts. One of PENDING, SUCCEEDED, FAILED, PARTIALLY_REFUNDED, REFUNDED.
amountCentsinteger
tipCentsinteger
methodLabelstring or nullVisa •••• 4242 once known.
failureReasonstring or nullWhy it failed, in words for the customer.
onlineChannelstring or nullTAP_TO_PAY or SAVED_CARD.
invoiceIdstring or null
invoiceIdentifierstring or null
quoteIdstring or null
quoteIdentifierstring or null
paidOnstring

A card payment taken in person: where it stands

GET /organizations/{organizationId}/payments/{paymentId}/in-person

The status of a Tap to Pay or saved-card payment (PENDING until Stripe settles it, SUCCEEDED, FAILED with a reason in words), its amount and the card (Visa •••• 4242). The app polls this after a tap. Read-only. Technicians: payments they took.

Permission: payments.read (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk read (OAuth scope servflo.read).

Permissionpayments.read
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolonlinePayments_inPersonPayment (toolset invoices)

Parameters

NameInTypeRequiredDescription
paymentIdpathstringYes

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payments/{paymentId}/in-person" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
idstring
identifierstringPAY-n.
statusenumPENDING until Stripe's webhook settles a Tap to Pay payment; SUCCEEDED counts. One of PENDING, SUCCEEDED, FAILED, PARTIALLY_REFUNDED, REFUNDED.
amountCentsinteger
tipCentsinteger
methodLabelstring or nullVisa •••• 4242 once known.
failureReasonstring or nullWhy it failed, in words for the customer.
onlineChannelstring or nullTAP_TO_PAY or SAVED_CARD.
invoiceIdstring or null
invoiceIdentifierstring or null
quoteIdstring or null
quoteIdentifierstring or null
paidOnstring

The customer's saved card for an invoice, and whether it can be charged

GET /organizations/{organizationId}/invoices/{invoiceId}/saved-card

The card the invoice's customer saved on the business's Stripe account (for memberships, from their portal), as brand, last four and expiry, and whether this caller may charge it now (allowed, else refusal in words: technicians need the owner's "Let technicians charge saved cards" switch; expired cards and bank accounts can't). Read-only. Technicians: their own jobs' invoices.

Permission: payments.read (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk read (OAuth scope servflo.read).

Permissionpayments.read
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolonlinePayments_savedCard (toolset invoices)

Parameters

NameInTypeRequiredDescription
invoiceIdpathstringYes

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices/{invoiceId}/saved-card" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
cardobject or null
card.labelstring or nullVisa •••• 4242.
card.brandstring or null
card.last4string or null
card.expMonthinteger or null
card.expYearinteger or null
card.expiredboolean
allowedboolean
refusalstring or null
balanceDueCentsinteger

Charge the customer's saved card for an invoice (moves money)

POST /organizations/{organizationId}/invoices/{invoiceId}/charge-saved-card

Money: charges the card the invoice's customer saved (GET /invoices/{invoiceId}/saved-card first) off-session on the business's Stripe account, for amountCents (default the balance due). Only after the person confirmed the amount with the customer. attemptKey is made once per confirmation: a retry with the same key charges once and returns the same payment. Writes a payment (channel SAVED_CARD, payment.started), settled from Stripe's answer: SUCCEEDED (payment.recorded) or FAILED with the reason (declined, expired, the bank asked the customer to confirm). Technicians only when the owner turned on "Let technicians charge saved cards" (409 otherwise). An AI caller always needs the person's approval in ServFlo.

Permission: payments.record (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk sensitive (OAuth scope servflo.sensitive).

Permissionpayments.record
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolonlinePayments_chargeSavedCard (toolset invoices)
ApprovalOver MCP, this tool always waits for your approval in ServFlo. See Approvals.

Parameters

NameInTypeRequiredDescription
invoiceIdpathstringYes

Request body

FieldTypeRequiredDescription
amountCentsintegerNoCents to take; defaults to the whole balance (an invoice) or the deposit asked for (a quote). At least 100 when less than what is due. 1–100000000.
attemptKeystringYesA random key made once per confirmation (a UUID): retries charge once. Up to 100 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices/{invoiceId}/charge-saved-card" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"attemptKey":"string"}'

Response

201: the answer's fields
FieldTypeDescription
idstring
identifierstringPAY-n.
statusenumPENDING until Stripe's webhook settles a Tap to Pay payment; SUCCEEDED counts. One of PENDING, SUCCEEDED, FAILED, PARTIALLY_REFUNDED, REFUNDED.
amountCentsinteger
tipCentsinteger
methodLabelstring or nullVisa •••• 4242 once known.
failureReasonstring or nullWhy it failed, in words for the customer.
onlineChannelstring or nullTAP_TO_PAY or SAVED_CARD.
invoiceIdstring or null
invoiceIdentifierstring or null
quoteIdstring or null
quoteIdentifierstring or null
paidOnstring

Text or email the customer a receipt for a card payment

POST /organizations/{organizationId}/payments/{paymentId}/receipt

Sends to the customer: a receipt for a Tap to Pay or saved-card payment that went through (amount, card, invoice or quote, PAY-n), by text (SMS) or EMAIL, through the engagement engine and its consent rules. Sent once per payment and channel (a repeat returns the first). 409 while the payment is still PENDING. In-person card payments don't trigger the automatic receipt; this is how the customer gets one. Technicians: payments they took.

Permission: invoices.send (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk sensitive (OAuth scope servflo.sensitive).

Permissioninvoices.send
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolonlinePayments_sendReceipt (toolset invoices)
ApprovalOver MCP, this tool asks for your OK first (in the AI app when it supports it, otherwise in ServFlo). See Approvals.

Parameters

NameInTypeRequiredDescription
paymentIdpathstringYes

Request body

FieldTypeRequiredDescription
channelenumYesOne of SMS, EMAIL.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payments/{paymentId}/receipt" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"channel":"SMS"}'

Response

200: the answer's fields
FieldTypeDescription
sentbooleanThe message was accepted for sending.
channelenumOne of SMS, EMAIL.
reasonstring or nullWhy it wasn't sent (no mobile number, no consent, no email).

On this page