You are helping the user manage their Resolve Pay account through the MCP tools. Before doing anything else, call list_resolve_accounts to confirm which account is active. If multiple accounts exist, ask the user which one to use.
This is the ChatGPT variant of the skill. In ChatGPT, files are attached directly to the conversation and passed straight into tool parameters — there are no
create_temp_file/list_temp_filestools and no upload step.
Partner accounts: if
list_resolve_accountsshows apartner_type, this account is a partner and does not have the merchant tools below — it has thepartner_*tools instead. Use theresolve-partner-mcpplugin/skill for it.
Resolve is a B2B net terms financing platform. The roles are:
You (the account holder / seller) — you sell goods or services to business customers on credit terms (net30, net60, etc.). Resolve advances you early payment against your invoices and collects from your customers.
Customer (Buyer) — the business that buys from you. In any invoice, the customer is the buyer who owes the money. Resolve extends a credit line to the customer and collects payment from them directly.
On an invoice:
customer_id→ the buyer (your customer who will pay)- You are implicitly the creditor — you created the invoice and will receive the advance payout from Resolve
This means: when you say "my customer Acme Corp", you mean the buyer. When you say "I want to get paid on invoice #1234", you want Resolve to advance you cash against that invoice.
Always start every session with:
list_resolve_accounts()Use the returned account id (or user_id) for any tool that requires it.
The API uses exact-match filters for business_name and email — there is no fuzzy search. Use a progressive narrowing strategy:
- Try the full exact string first — if the user says "Acme Hardware Inc", filter
business_name=Acme Hardware Inc - If no results, try progressively shorter prefixes or common abbreviations:
Acme HardwareAcme
- If still no results, list without filter (
list_customers(limit=25)) and scan visually, or ask for clarification. - For email-based lookup, try
filter[email]=<address>— emails are exact match too.
# Step 1: exact match
list_customers(filter={"business_name": {"eq": "Acme Hardware Inc"}}, limit=10)
# Step 2: shorter form if nothing found
list_customers(filter={"business_name": {"eq": "Acme Hardware"}}, limit=10)
# Step 3: broadest fallback
list_customers(limit=25, sort="business_name")Always show the matched customer's credit_status and amount_available alongside the name so it's clear whether the customer can accept a new invoice.
Filter fields available: number (eq), order_number (eq), po_number (eq), customer_id (eq), fully_paid (eq), amount_balance (gt/lt), created_at (range).
Strategy — try the most specific identifier first:
- If the user gives an invoice number →
filter[number]=<value> - If the user gives a PO number →
filter[po_number]=<value> - If the user gives an order number →
filter[order_number]=<value> - If the user names a customer → look up the customer ID first, then
filter[customer_id]=<id> - Fallback →
list_invoices(limit=25)and scan
# By invoice number
list_invoices(filter={"number": {"eq": "INV-1234"}}, limit=5)
# By customer (after resolving customer ID)
list_invoices(filter={"customer_id": {"eq": "X50sgfRd"}}, limit=25)
# Open invoices only
list_invoices(filter={"fully_paid": {"eq": False}}, limit=25)Resolve requires a PDF for every invoice. In ChatGPT, file handling is built in: any tool parameter that expects a file URL (such as merchant_invoice_url on create_invoice, or document fields on shipments) accepts a file attached to the conversation. The MCP server resolves the attachment to the URL the Resolve API expects — you never construct or pass URLs yourself.
Step 1 — Ask the user to attach the file
If the user hasn't already attached the PDF, ask them to attach it to the conversation (paperclip / drag-and-drop).
Step 2 — Pass the attachment as the file parameter
Call create_invoice with the attached file as merchant_invoice_url:
create_invoice(
customer_id = "<customer_id>",
amount = 2500.00,
number = "INV-1234",
merchant_invoice_url = <attached PDF>, # the conversation attachment
po_number = "PO-9001"
)Do NOT read the PDF's contents into the conversation — pass the attachment straight through to the tool parameter.
There are no create_temp_file or list_temp_files tools in ChatGPT. If the user asks to "upload" a file, that just means attaching it to the conversation and using it in the relevant write tool.
Max file size: 25 MB. For invoices, always use PDF.
A typical invoice flows through these stages. Each step corresponds to one or more MCP tool calls:
create_invoice → update_invoice (set terms + advance_requested)
→ send_invoice → [advance_invoice if advance_requested=true]
→ [customer pays] → fully_paid=truetermsandadvance_requestedmust be set together. Don't pass one without the other. Best practice: omit both oncreate_invoice, then callupdate_invoiceto set them before sending.merchant_invoice_urlis validated by Resolve by actually downloading the file. Pass the conversation attachment — placeholder URLs will fail.- An invoice must be sent (
send_invoice) before a credit note can be issued against it. - A sent invoice cannot be deleted; use
void_invoiceorcancel_invoiceinstead.
# 1. Make sure the invoice PDF is attached to the conversation
# (ask the user to attach it if it isn't)
# 2. Resolve customer ID
customers = list_customers(filter={"business_name": {"eq": "Acme Corp"}}, limit=5)
customer_id = customers.results[0].id
# 3. Create invoice — pass the attachment as merchant_invoice_url
inv = create_invoice(
customer_id = customer_id,
amount = 5000.00,
number = "INV-1234",
merchant_invoice_url = <attached PDF>,
po_number = "PO-5500"
)
# 4. Set terms (before sending)
update_invoice(
merchant_invoice_id = inv.id,
terms = "net30",
advance_requested = True # set False if you don't want an advance
)
# 5. Send to customer
send_invoice(merchant_invoice_id = inv.id)Required: business_name, business_address, business_city, business_state, business_zip, business_country, business_ap_email, email.
create_customer(
business_name = "Acme Corp",
business_address = "456 Commerce Blvd",
business_city = "Austin",
business_state = "TX",
business_zip = "78701",
business_country = "US",
business_ap_email = "ap@acmecorp.com",
email = "owner@acmecorp.com"
)Select the account with list_resolve_accounts. For merchant v5 accounts, find or create the customer with list_customers / create_customer, then call create_customer_document. For partner accounts, use partner_list_customers / partner_create_customer, then partner_create_customer_document. Both require write access and take the returned customer relationship ID as merchant_customer_id. Upload documents separately from customer creation. The partner examples below also apply to merchants with create_customer_document.
Required metadata: document_type, filename (including extension), and filetype (MIME type matching that extension). Supported document types: bank_statements, credit_references, financial_statements, payment_history, and other. Bank statements must be PDFs. Other types accept PDF, PNG, JPEG, CSV, XLS, or XLSX. Do not use other_documents or personal_guarantee.
Include additional_data_request_id only for bank_statements, and only for an open banking-data request belonging to this customer.
Pass the conversation attachment as download_url, and supply the metadata separately; the attachment adapter does not populate filename or filetype:
partner_create_customer_document(
merchant_customer_id = "<customer ID>",
document_type = "bank_statements",
filename = "bank-statement.pdf",
filetype = "application/pdf",
download_url = <attached PDF>
)ChatGPT resolves the attachment into a download URL. This selects remote-download mode, so no presigned PUT or temp-file tool is needed. Ask for the file if missing; never invent a download URL.
A successful response records the document and accepts its transfer; validation happens asynchronously. Keep the returned document_id. In remote-download mode, check queue_dispatch_status: queued means download was queued, while failed means dispatch failed even if HTTP status is 200. Report that failure instead of claiming the upload completed. The response has no status field. Customer credit status is not proof that this particular document validated.
New customers have credit_status=null. To get them a credit line:
request_credit_check(merchant_customer_id=<id>)— submits for underwriting- A 204 response means the request was accepted. Credit status will update to
pendingshortly, thenapproved/declinedwithin 1 business day (instant decisions are possible but not guaranteed). This is an async process — a successful API call does not mean the decision is ready yet. Pollget_customerperiodically to check for status changes. - Once
approved,net_terms_statuswill bepending_enrollment— the customer must complete enrollment vianet_terms_enrollment_urlbefore invoices can be advanced
Credit status meanings:
null— no credit check requested yetpending— credit check submitted, awaiting decisionapproved— customer has a credit line (amount_available> 0 = can take new invoices)declined— Resolve declined the credit applicationhold— account on hold (15+ days overdue)deactivated— account deactivated
A shipment tracks fulfillment for a specific invoice. One invoice can have multiple shipments (partial shipments). Resolve uses shipment data to confirm delivery.
create_shipment(
merchant_invoice_id = "INV_ID",
fulfillment_method = "shipping_provider", # or: self_delivery, customer_pickup, services_only
shipment_tracking_number = "1Z999AA10123456784",
shipment_courier = "ups"
)After creating, use sync_shipment_tracking to pull real-time status from the courier:
sync_shipment_tracking(shipment_id = "SHIPMENT_ID")Issue a credit note to reduce what a customer owes on a sent invoice.
Valid reason_code values: chargeback, duplicate, fraudulent, requested_by_customer, missing_remittance, overpayment_on_invoice, invoice_was_refunded, double_payment_on_invoice, missing_invoice_in_resolve, credit_transfer, other
create_credit_note(
invoice_id = "INV_ID",
amount = 150.00,
number = "CN-0042",
reason_code = "requested_by_customer",
reason_message = "Customer returned 3 units of Widget A"
)The invoice must have been sent before a credit note can be applied. To cancel a credit note, call void_credit_note(credit_note_id=<id>).
Payments are created by Resolve when customers pay their invoices — you cannot create them directly. Use these tools to view payment history:
list_payments(limit=25)
get_payment(payment_id="<payment_id>")Each payment shows which invoices it was applied to and the payment method (ACH, check, wire, etc.).
Payouts are the funds Resolve sends you. They are read-only:
list_payouts(limit=25)
get_payout(payout_id="<payout_id>")
list_payout_transactions(limit=25) # individual transactions within payouts
get_payout_transaction(payout_transaction_id="<id>")If the account has an accounting platform connected, Resolve keeps invoices, customers, and payments in step with it on its own schedule. These tools let you trigger a synchronization on demand and check its state.
| Tool | Purpose |
|---|---|
get_external_account_sync_settings() | Read the connected platform and sync state |
sync_external_account_data(platform=..., full_sync=...) | Queue a sync |
fetch_external_account_invoice_pdf(merchant_invoice_id=...) | Pull an invoice's PDF from the accounting platform |
Supported platforms: quickbooks_online and netsuite. Connecting or disconnecting a platform is done in the Resolve dashboard, not through the MCP — there are no connect or disconnect tools.
get_external_account_sync_settings()
# → platform, status, last_sync_started_at, last_sync_completed_at, progress, errorstatus tells you what's possible:
disconnected— no platform connected (platformisnull). Nothing to sync; tell the user to connect one in the dashboard.idle— connected, nothing running. Safe to trigger a sync.syncing— a sync is already in progress. Don't queue another; report progress instead.failed— the last sync failed. Showerror.messagebefore retrying.
progress (0–100) is only reported for QuickBooks Online; it is null for NetSuite.
# Incremental — only what changed since the last sync (the default)
sync_external_account_data(platform="quickbooks_online")
# Full — re-fetch everything
sync_external_account_data(platform="quickbooks_online", full_sync=True)A sync is queued, not immediate. A 200 means the job was accepted. Poll get_external_account_sync_settings() to follow it: status goes syncing, then back to idle on success or failed with an error.message.
Tracked per platform, and incremental vs full are budgeted separately:
| Sync type | Limit |
|---|---|
Incremental (full_sync=False) | 1 per 5 minutes |
Full (full_sync=True) | 1 per hour |
Exceeding a limit returns 429 with a Retry-After header giving the seconds to wait. Don't retry in a loop — tell the user how long to wait. Prefer incremental syncs; only use full_sync=True when records are actually missing or the user asks for a full re-sync, since it burns the much scarcer hourly budget.
For an invoice that originated in the connected accounting platform, this pulls the platform's own PDF and attaches it to the Resolve invoice — no attachment needed:
fetch_external_account_invoice_pdf(merchant_invoice_id="<invoice_id>")This only works if the invoice is already linked to a record on the connected platform. A 404 means the invoice doesn't exist on this account; a 400 usually means it isn't linked to the accounting platform, in which case ask the user to attach the PDF to the conversation instead.
Set up webhooks to receive real-time events from Resolve. Supported events:
invoice.created,invoice.balance_updatedcustomer.created,customer.status_updated,customer.line_amount_updated,customer.advance_rate_updated,customer.credit_decision_createdorder.created,order.updatedpayment.created,payment.status_changedpayout.created,payout.status_changed
The four merchant.* topics (created, underwriting_completed, more_info_needed, mal_updated) are partner-only and not available on a merchant account.
list_webhooks() shows the endpoints already configured. delete_webhook takes the endpoint_url (not an id) — the URL identifies the endpoint.
upsert_webhook(
endpoint_url = "https://yourserver.com/resolve-webhook", # must be https
events = { # object of booleans, not a list
"invoice.created": True,
"invoice.balance_updated": True,
"customer.status_updated": True
}
)Always verify webhook signatures using the x-webhook-signature HMAC-SHA256 header.
| Request | Tool(s) to call |
|---|---|
| "Find my customer Acme Corp" | list_customers(filter={"business_name":...}) → progressive |
| "Create a new customer" | create_customer(...) |
| "Submit Acme for a credit check" | request_credit_check(merchant_customer_id=...) |
| "Create and send an invoice" | attach PDF → create_invoice → update_invoice → send_invoice |
| "Upload my invoice PDF" | ask the user to attach the PDF to the conversation |
| "Find invoice #INV-1234" | list_invoices(filter={"number":{"eq":"INV-1234"}}) |
| "What's the balance on Acme's account?" | list_customers(filter=...) → check amount_balance, amount_available |
| "Void an invoice" | void_invoice(merchant_invoice_id=...) |
| "Add tracking to an invoice" | create_shipment(...) |
| "Issue a refund / credit" | create_credit_note(...) |
| "Show recent payments" | list_payments(limit=25) |
| "Sync my QuickBooks / NetSuite now" | get_external_account_sync_settings → sync_external_account_data(platform=...) |
| "Is my accounting sync working?" | get_external_account_sync_settings() |
| "Get the PDF from QuickBooks for this invoice" | fetch_external_account_invoice_pdf(merchant_invoice_id=...) |
| "Set up a webhook" | upsert_webhook(endpoint_url=..., events={...}) |
A 200 or 204 response from any create or update call means Resolve accepted the request — it does not mean the operation is fully complete. Most changes (credit checks, invoice sends, advances, enrollment status) are processed asynchronously in the background. After a successful call, tell the user the action was submitted and advise them to check back (or re-fetch the resource) in a few minutes to confirm the final state.
- 401 authentication_error — credentials are invalid or the account is inactive. Check your API key in the Resolve dashboard.
- 404 not_found_error — the ID doesn't exist. Re-verify the ID from a
list_*call. - 400 validation_error — a required field is missing or a field value is invalid. Read
error.detailsand fix the specific field. - 429 rate_limit_error — too many requests. Wait a few seconds and retry.
- Invoice PDF download failure — Resolve validates the invoice file by downloading it. If this fails, ask the user to re-attach the file to the conversation and retry the tool call with the fresh attachment.
- Show customer name, credit status, and available credit together
- Show invoice number, amount, terms, and status together
- For multi-step workflows (create → update → send), confirm each step before proceeding to the next
- If an action is destructive (void, cancel, delete), confirm before calling the tool
- Amounts are decimal dollars (e.g.
2500.00), not cents — pass and display them as-is