Skip to main content
This guide is for MSP staff and developers who connect a billing or quoting system that has no MSPortal integration. Each call carries one record and its PDF: Uploaded invoices and quotes sort, filter, search, export and bulk-download with the synced ones. Push the same record again to update it, for example when an invoice is paid.

Before you start

  • Create an API key. Go to Settings > Integrations, find the API Access card, click Manage API Keys, then Create New Key. Creating keys needs the Integrations permission at Manage. Pick these scopes:
    • Write Financial (financial.write) for invoices and quotes.
    • Write Documentation (documentation.write) for orders and other documents.
    • Read Companies (companies.read) to look up company IDs.
    • Optional: Read Financial (financial.read) to list what you uploaded.
  • Create the key as an MSP staff user with write access. A key acts as the user who created it. Invoice and quote uploads need Integrations at Write. Document uploads need the Documentation settings permission at Write. Admin roles have both. A key created by a company (portal) user cannot upload.
  • Use your region’s address. It is the address you sign in to MSPortal at: US tenants call https://app.msportal.ai, EU tenants https://eu.msportal.ai. The examples use the EU address.
  • No PSA or quoting integration is needed. The Invoices and Quotes pages appear after your first upload. To let a client’s portal users open them, give their role the Invoices, Quotes or Documentation permission.
  • The examples use curl and jq. Any language that can send JSON over HTTPS works the same way.
Every request sends the key as a Bearer token:
The API Reference tab lists every field, under Financial and Documentation. Leave out optional fields you have no value for. An empty string ("") in a text field is refused with a 400.

Find the company ID

Each record belongs to one company (your client). List your companies once, match them to your clients by name, and store each MSPortal id next to the client in your system:
While pagination.hasNextPage is true, fetch the next page (page=2, and so on). Use the matching company’s id as companyId below.

Upload an invoice

Send the invoice and its PDF in one request. The PDF goes in pdf.contentBase64 as standard base64 (no data: prefix), up to 3,000,000 bytes.
The answer is 201 with the MSPortal id. Store it if you may want to delete the invoice later.

Mark an invoice paid

Send the same externalId again with the new values. Leave out pdf to keep the stored PDF, or send a new one to replace it. The answer is 200 with "created": false.
Every push replaces the invoice’s values, so send all the fields you want to keep, not only the ones that changed.

Upload a quote

Push the same externalId again to change the stage or replace the PDF. As with invoices, every push replaces the quote’s values; a push without quoteDate keeps the stored date.

Upload an order or another document

Orders have no table of their own, so they go into the company’s Documentation library. PDF, DOCX, XLSX, XLS and CSV files up to 50 MiB are accepted. A document upload takes three calls: prepare, send the bytes, finalize.
Finalize answers 201, and the document appears in the company’s Documentation library.
  • companyId is required. A document without a company would be visible to every client, so it is refused.
  • fileSize must be the exact size in bytes, and fileName must end in the file’s extension (.pdf, .docx, .xlsx, .xls or .csv) and must not contain ...
  • To file orders in their own folder, click New folder on the Documentation page once, read the folder’s id from GET /documentation/folders, and pass it as folderId. A folder without a company works for every client.
  • If finalize times out, send it again with the same documentId and body: it returns the same document, never a duplicate. A retry that names a different company or folder answers 409.
  • An uploadUrl accepts one file. If the bytes you sent had the wrong size or type, finalize answers 400; start again with a new prepare call.
  • Documents uploaded this way can’t be deleted through the API yet.

Delete an upload

Delete an invoice or quote you uploaded by its MSPortal id. The PDF is removed with it. Invoices synced from your PSA can’t be deleted this way and answer 404.

Retries

  • Invoice and quote uploads are keyed by externalId, so sending the same request again is always safe: it updates the record instead of creating a second one.
  • Document uploads are safe to finalize again with the same documentId. If a prepare call fails, start over with a new prepare.

Errors

Every error answers with a JSON body. error is a readable message, code a stable value to branch on, and requestId identifies the call for support. When fields are invalid, details names each one:
A rule checked after the fields, such as a missing PDF on the first upload, comes back as { "error": "a PDF is required", "code": "VALIDATION_ERROR", "requestId": "…" }.