Skip to content
Last updated

Resolve MCP (ChatGPT)

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_files tools and no upload step.

Partner accounts: if list_resolve_accounts shows a partner_type, this account is a partner and does not have the merchant tools below — it has the partner_* tools instead. Use the resolve-partner-mcp plugin/skill for it.


Understanding Resolve: B2B Net Terms

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.


Account Setup

Always start every session with:

list_resolve_accounts()

Use the returned account id (or user_id) for any tool that requires it.


Searching for Customers

The API uses exact-match filters for business_name and email — there is no fuzzy search. Use a progressive narrowing strategy:

  1. Try the full exact string first — if the user says "Acme Hardware Inc", filter business_name=Acme Hardware Inc
  2. If no results, try progressively shorter prefixes or common abbreviations:
    • Acme Hardware
    • Acme
  3. If still no results, list without filter (list_customers(limit=25)) and scan visually, or ask for clarification.
  4. 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.


Searching for Invoices

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:

  1. If the user gives an invoice number → filter[number]=<value>
  2. If the user gives a PO number → filter[po_number]=<value>
  3. If the user gives an order number → filter[order_number]=<value>
  4. If the user names a customer → look up the customer ID first, then filter[customer_id]=<id>
  5. 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)

Attaching an Invoice PDF (ChatGPT File Attachments)

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-by-step

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.


Invoice Lifecycle

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=true

Key rules

  • terms and advance_requested must be set together. Don't pass one without the other. Best practice: omit both on create_invoice, then call update_invoice to set them before sending.
  • merchant_invoice_url is 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_invoice or cancel_invoice instead.

Creating and sending an invoice (full example)

# 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)

Customer Management

Creating a new customer

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"
)

Customer supporting documents

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.

Credit workflow

New customers have credit_status=null. To get them a credit line:

  1. request_credit_check(merchant_customer_id=<id>) — submits for underwriting
  2. A 204 response means the request was accepted. Credit status will update to pending shortly, then approved / declined within 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. Poll get_customer periodically to check for status changes.
  3. Once approved, net_terms_status will be pending_enrollment — the customer must complete enrollment via net_terms_enrollment_url before invoices can be advanced

Credit status meanings:

  • null — no credit check requested yet
  • pending — credit check submitted, awaiting decision
  • approved — customer has a credit line (amount_available > 0 = can take new invoices)
  • declined — Resolve declined the credit application
  • hold — account on hold (15+ days overdue)
  • deactivated — account deactivated

Shipments

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")

Credit Notes

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

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

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>")

Accounting Sync (External Sync)

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.

ToolPurpose
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.

Always check the state first

get_external_account_sync_settings()
# → platform, status, last_sync_started_at, last_sync_completed_at, progress, error

status tells you what's possible:

  • disconnected — no platform connected (platform is null). 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. Show error.message before retrying.

progress (0–100) is only reported for QuickBooks Online; it is null for NetSuite.

Triggering a sync

# 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.

Rate limits

Tracked per platform, and incremental vs full are budgeted separately:

Sync typeLimit
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.

Fetching an invoice PDF from the accounting platform

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.


Webhooks

Set up webhooks to receive real-time events from Resolve. Supported events:

  • invoice.created, invoice.balance_updated
  • customer.created, customer.status_updated, customer.line_amount_updated, customer.advance_rate_updated, customer.credit_decision_created
  • order.created, order.updated
  • payment.created, payment.status_changed
  • payout.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.


Common Requests → Tool Mapping

RequestTool(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={...})

Async Operations

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.


Error Handling

  • 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.details and 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.

Output Style

  • 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