API reference

Invoices

Invoices from suppliers to buyers, from arrival through matching, approval and payment.

More about this resource

A supplier's invoice is created by flipping a purchase order (source: "po-flip") or by entering it for a connected buyer (source: "manual", see CreateInvoice), or sends it from its billing system with an API key (source: "feed", see SubmitFeedInvoice) once that buyer turns on automated invoices. A buyer can also record one directly (source: "direct", see SubmitInvoice), as JSON, cXML (InvoiceDetailRequest), or UBL 2.1 (<Invoice>). Every invoice lives in the buyer's workspace and moves through matching, approval, and settlement. An invoice is "pending" when there is a real usage figure to match it against: a PO, and a live billing connection for the PO's provider with a synced invoice. usageAmount, usageCurrency and tolerancePct are present only then, and on the "matched" or "discrepancy" result. Otherwise it is "unverified", with unverifiedReason "no_purchase_order" or "no_billing_connection" (buyer view only; absent on older invoices, which read as "no_purchase_order"), or "debit_note" for a debit note on a cloud provider's PO, which adds to an invoice already billed and so can't be compared with the month's bill. A Supplier caller sees matchStatus as it is, but never unverifiedReason or the usage fields. An unverified invoice is never matched and never approved automatically: a person reviews it with ResolveInvoiceDiscrepancy.

List invoices

Lists invoices, and optionally credit and debit notes.

GET/v1/workspaces/{regionId}/invoices

Parameters

  • fromstring

    ISO 8601 date/timestamp - only items created on or after this are returned.

  • tostring

    ISO 8601 date/timestamp - only items created on or before this are returned.

  • documentTypestring

    By default what's paid: invoices and the debit notes that amend them. invoice (invoices alone), debit_note, credit_note (the same items as ListCreditNotes), or all (every kind, newest first). Every item carries documentType. Anything else is 400 DOCUMENT_TYPE_INVALID.

Returns

200 OK with the object shown in the response.

More details

Lists invoices, in no particular order - or, with documentType, credit notes or both: invoices and credit notes are one resource of statements, each item saying which it is in documentType. A Buyer caller gets the invoices its workspace received; a Supplier caller gets the invoices it issued to connected buyers, whichever buyer workspace each lives in. Optionally scoped to a createdAt range via from/to - pass a calendar month's bounds to get a per-month view. A Supplier caller gets a reduced view of each invoice (see GetInvoice) - the buyer's matching internals and full approval workflow are withheld.

Errors

  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
GET /v1/workspaces/{regionId}/invoices
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
[
   {
      "documentType": "invoice" | "debit_note",
      "amendsInvoiceId": "string",
      "amendsExternalInvoiceNumber": "string",
      "debitNoteIds": [ "string" ],
      "replacesInvoiceIds": [ "string" ],
      "replacedByInvoiceIds": [ "string" ],
      "replacement": { "creditedInFull": boolean, "uncredited": number },
      "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
      "id": "string",
      "po": "string",
      "poLineNo": number,
      "billsNoPoLine": true,
      "buyerOrgId": "string",
      "supplierOrgId": "string",
      "supplier": "string",
      "amount": number,
      "currency": "string",
      "usageAmount": number,
      "usageCurrency": "string",
      "tolerancePct": number,
      "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
      "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
      "discrepancyReason": "amount" | "currency",
      "paid": boolean,
      "dueInDays": number,
      "approval": {
         "requester": "string",
         "requesterEmployeeKey": "string",
         "groups": [
           {
             "id": "string",
             "mode": "sequential" | "parallel",
             "label": "string",
             "status": "pending" | "approved" | "rejected",
             "stages": [
               { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
             ]
           }
         ],
         "groupIndex": number,
         "status": "pending" | "approved" | "rejected",
         "history": [
           { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
         ],
         "autonomy": boolean,
         "selfApproved": boolean
       },
      "createdAt": "string",
      "lastRemindedAt": "string",
      "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
      "replacesInvoiceId": "string",
      "source": "po-flip" | "direct" | "manual" | "feed",
      "submittedFormat": "json" | "cxml" | "ubl",
      "submittedBy": "string",
      "externalInvoiceNumber": "string",
      "lineItems": [
         { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
      ],
      "adjustments": [
         { "type": "discount" | "charge", "description": "string", "amount": number }
      ],
      "taxAmount": number,
      "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
      "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
      "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
      "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
      "rateWarnings": [
         { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
      ],
      "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
      "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
      "invoiceKind": "prepayment",
      "creditNoteIds": [ "string" ],
      "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
      "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
      "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
   }
]

Get invoice

Retrieves one invoice.

GET/v1/workspaces/{regionId}/invoices/{invoiceId}

Parameters

  • invoiceIdstringRequired

    The invoice identifier, or a credit note's (CN-…).

  • buyerstring

    Supplier callers only, and only needed after a 409 *_ID_AMBIGUOUS: the buyer's Workspace ID (ending in .buyer) or region id, from the 409's buyers list. Only that buyer's document is considered; none there is a 404. A .supplier Workspace ID is 400 BUYER_QUALIFIER_INVALID ("The buyer parameter must be one buyer's Workspace ID (ending in .buyer), from the buyers list in the 409 response."). Ignored for a Buyer caller.

Returns

200 OK with the object shown in the response.

More details

Fetches a single invoice by id - or, given a credit note's id (CN-…), that credit note, shaped as in GetCreditNote; documentType says which. For a Buyer caller, regionId is the invoice's own workspace. For a Supplier caller, regionId is the supplier's own workspace and the invoice is looked up among the org's incoming invoices regardless of which buyer workspace issued it - the same lookup ListSupplierWorkspace uses. GET …/invoices/{invoiceId}/pdf takes the same buyer query parameter.

Response fields

  • replacesInvoiceIdsArray<string>

    Credit and rebill: the invoices and debit notes this invoice replaces. Absent when none.

  • replacedByInvoiceIdsArray<string>

    The invoices that replace this one. Absent when none.

  • replacementobject

    Buyer, single-invoice reads only, on a replaced invoice that wasn't rejected: creditedInFull, and uncredited - its amount less its accepted and pending credit notes, in its currency. While uncredited is above 0, paying it as well as its replacement pays twice, and RecordPayment, StartStripePayment and ExportInvoicesForPayment ask for confirmReplaced.

  • uncreditedOriginalsArray

    Buyer, single-invoice reads only, on a replacement: the invoices it replaces that their credit notes don't cover in full. Absent when none.

Errors

  • 404 Not Found - No invoice with this id exists in the caller's workspace.
  • 409 Conflict - Supplier callers only. Code INVOICE_ID_AMBIGUOUS: more than one of your buyers holds an invoice from you with this id (ids are unique only within a buyer), so Pleaxy won't guess which one you mean. Nothing is read or changed. The body's buyers lists each buyer's Workspace ID and name: repeat the call with ?buyer=<that Workspace ID>.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
GET /v1/workspaces/{regionId}/invoices/{invoiceId}
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Get invoice activity

Returns an invoice's history, oldest first.

GET/v1/workspaces/{regionId}/invoices/{invoiceId}/activity

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

  • buyerstring

    Supplier callers only, and only needed after a 409 *_ID_AMBIGUOUS: the buyer's Workspace ID (ending in .buyer) or region id, from the 409's buyers list. Only that buyer's document is considered; none there is a 404. A .supplier Workspace ID is 400 BUYER_QUALIFIER_INVALID ("The buyer parameter must be one buyer's Workspace ID (ending in .buyer), from the buyers list in the 409 response."). Ignored for a Buyer caller.

Returns

200 OK with the object shown in the response.

More details

An invoice's history, oldest first: its arrival, the checks and matches run on it, approval steps, rejection, payments, reminders and comments. Each entry has a kind and structured data, never a sentence, so you can word it yourself. Most entries are read off the invoice itself, so an older invoice has a history too. A Supplier caller gets the invoice's arrival, its outcome, recorded payments, reminders and shared comments only.

Response fields

  • idstring

    Stable for the same history, so you can tell which entries you have already seen.

  • kind, datastring, object

    received: data.source, enteredBy, submittedFormat, fileName. corrects: data.invoiceId, the rejected invoice it replaces. control_findings (buyer only): data.codes. match (buyer only): data.result (matched, discrepancy or unverified) and data.reason. sent_for_approval (buyer only): data.from (discrepancy or unverified). approval (buyer only): data.action, data.stage, and data.to for a reassignment. approved. rejected: data.reasonCode and data.reason. payment: data.action, method, paymentDate, reference. reminder: data.autonomy when the workspace sent it on its own. comment: the comment itself, in comment.

Errors

  • 404 Not Found - No invoice with this id exists in the caller's workspace.
  • 409 Conflict - Supplier callers only. Code INVOICE_ID_AMBIGUOUS: more than one of your buyers holds an invoice from you with this id (ids are unique only within a buyer), so Pleaxy won't guess which one you mean. Nothing is read or changed. The body's buyers lists each buyer's Workspace ID and name: repeat the call with ?buyer=<that Workspace ID>.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
GET /v1/workspaces/{regionId}/invoices/{invoiceId}/activity
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/activity" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
{
   "entries": [
      {
         "id": "string",
         "at": "string",
         "kind": "received" | "corrects" | "control_findings" | "match" | "sent_for_approval" | "approval" | "approved" | "rejected" | "payment" | "reminder" | "comment",
         "actorName": "string",
         "actorSide": "buyer" | "supplier" | "system",
         "data": { },
         "comment": {
            "id": "string",
            "documentKind": "invoice" | "purchase_order",
            "documentId": "string",
            "body": "string",
            "visibility": "internal" | "shared",
            "authorSide": "buyer" | "supplier",
            "authorKey": "string",
            "authorName": "string",
            "authorOrgName": "string",
            "mentions": [ { "employeeKey": "string", "name": "string" } ],
            "createdAt": "string",
            "deletedAt": "string",
            "viewerIsAuthor": boolean
         }
      }
   ]
}

List invoice comments

Lists the comments on an invoice.

GET/v1/workspaces/{regionId}/invoices/{invoiceId}/comments

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

  • buyerstring

    Supplier callers only, and only needed after a 409 *_ID_AMBIGUOUS: the buyer's Workspace ID (ending in .buyer) or region id, from the 409's buyers list. Only that buyer's document is considered; none there is a 404. A .supplier Workspace ID is 400 BUYER_QUALIFIER_INVALID ("The buyer parameter must be one buyer's Workspace ID (ending in .buyer), from the buyers list in the 409 response."). Ignored for a Buyer caller.

Returns

200 OK with the object shown in the response.

More details

The comments on an invoice, oldest first: your own side's internal ones and every shared one. A comment its author deleted stays in the thread with an empty body, so it still shows that something was said and when. Same lookup as GetInvoice, so a supplier reaches the invoices it issued.

Response fields

  • visibilitystring

    internal: the author's own side only. shared: both the buyer and the supplier read it.

  • authorKey, mentionsstring, Array

    Only on your own side's comments: who wrote it by employee key, and whom it named. The other side sees authorName and authorOrgName.

  • bodystring

    Plain text with its line breaks. Empty once the author deleted it; deletedAt then says when.

  • viewerIsAuthorboolean

    Whether the caller wrote it, and so may delete it. Always false for an API key.

Errors

  • 404 Not Found - No invoice with this id exists in the caller's workspace.
  • 409 Conflict - Supplier callers only. Code INVOICE_ID_AMBIGUOUS: more than one of your buyers holds an invoice from you with this id (ids are unique only within a buyer), so Pleaxy won't guess which one you mean. Nothing is read or changed. The body's buyers lists each buyer's Workspace ID and name: repeat the call with ?buyer=<that Workspace ID>.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
GET /v1/workspaces/{regionId}/invoices/{invoiceId}/comments
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/comments" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
{
   "comments": [
      {
         "id": "string",
         "documentKind": "invoice" | "purchase_order",
         "documentId": "string",
         "body": "string",
         "visibility": "internal" | "shared",
         "authorSide": "buyer" | "supplier",
         "authorKey": "string",
         "authorName": "string",
         "authorOrgName": "string",
         "mentions": [ { "employeeKey": "string", "name": "string" } ],
         "createdAt": "string",
         "deletedAt": "string",
         "viewerIsAuthor": boolean
      }
   ]
}

Add invoice comment

Adds a comment to an invoice.

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/comments

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

  • buyerstring

    Supplier callers only, and only needed after a 409 *_ID_AMBIGUOUS: the buyer's Workspace ID (ending in .buyer) or region id, from the 409's buyers list. Only that buyer's document is considered; none there is a 404. A .supplier Workspace ID is 400 BUYER_QUALIFIER_INVALID ("The buyer parameter must be one buyer's Workspace ID (ending in .buyer), from the buyers list in the 409 response."). Ignored for a Buyer caller.

Body

  • bodystringRequired

    Plain text, up to 2,000 characters. Line breaks are kept; control, bidirectional-text and invisible characters are removed.

  • visibility"internal" | "shared"Required

    internal: your own team only. shared: the other side reads and answers it too. A supplier's comments are always shared.

  • mentionsstring[]

    Up to 10 employee keys of people in your own workspace to notify (see ListEmployees). The notification never carries the comment's text.

Returns

201 Created with the object shown in the response.

More details

Comments on an invoice: internal to your team, or shared with the other side - for example a buyer asking the supplier about a line. Comments are never edited. The people you mention, your side's earlier commenters and, on a shared comment, the other side are notified (invoice_comment); the notification never carries the text.

Signed-in session only. An API key gets 403 HUMAN_SESSION_REQUIRED.

Errors

  • 400 Bad Request - An empty body ("Write something before you post the comment."), more than 2,000 characters, more than 10 mentions, a mention who isn't in your workspace, a visibility other than internal or shared, a supplier's internal comment ("A supplier's comments are always shared with the buyer."), or a shared comment on a document whose supplier isn't on Pleaxy (post it as internal instead).
  • 403 Forbidden - Code HUMAN_SESSION_REQUIRED: the caller is an API key ("Comments are written by people. Sign in to Pleaxy to post one.").
  • 404 Not Found - No invoice with this id exists in the caller's workspace.
  • 409 Conflict - Supplier callers only. Code INVOICE_ID_AMBIGUOUS: more than one of your buyers holds an invoice from you with this id (ids are unique only within a buyer), so Pleaxy won't guess which one you mean. Nothing is read or changed. The body's buyers lists each buyer's Workspace ID and name: repeat the call with ?buyer=<that Workspace ID>.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/comments
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/comments" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "body": "string",
   "visibility": "internal" | "shared",
   "mentions": [ "string" ]
}
Response · 201 Created
{
   "id": "string",
   "documentKind": "invoice" | "purchase_order",
   "documentId": "string",
   "body": "string",
   "visibility": "internal" | "shared",
   "authorSide": "buyer" | "supplier",
   "authorKey": "string",
   "authorName": "string",
   "authorOrgName": "string",
   "mentions": [ { "employeeKey": "string", "name": "string" } ],
   "createdAt": "string",
   "deletedAt": "string",
   "viewerIsAuthor": boolean
}

Delete invoice comment

Deletes one of your comments on an invoice.

DELETE/v1/workspaces/{regionId}/invoices/{invoiceId}/comments/{commentId}

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

  • commentIdstringRequired

    The comment, e.g. "CMT-1760313600000-3f9a1c2e".

  • buyerstring

    Supplier callers only, and only needed after a 409 *_ID_AMBIGUOUS: the buyer's Workspace ID (ending in .buyer) or region id, from the 409's buyers list. Only that buyer's document is considered; none there is a 404. A .supplier Workspace ID is 400 BUYER_QUALIFIER_INVALID ("The buyer parameter must be one buyer's Workspace ID (ending in .buyer), from the buyers list in the 409 response."). Ignored for a Buyer caller.

Returns

200 OK with the object shown in the response.

More details

Takes back your own comment: its text is removed and the thread keeps that a comment was made. Deleting it again changes nothing.

Signed-in session only. An API key gets 403 HUMAN_SESSION_REQUIRED.

Errors

  • 403 Forbidden - Code HUMAN_SESSION_REQUIRED: the caller is an API key ("Comments are written by people. Sign in to Pleaxy to post one.").
  • 403 Forbidden - The comment isn't yours ("Only the person who wrote a comment can delete it.").
  • 404 Not Found - No comment with this id that you can see on this document ("Comment {commentId} not found."), or no such document.
  • 409 Conflict - Supplier callers only. Code INVOICE_ID_AMBIGUOUS: more than one of your buyers holds an invoice from you with this id (ids are unique only within a buyer), so Pleaxy won't guess which one you mean. Nothing is read or changed. The body's buyers lists each buyer's Workspace ID and name: repeat the call with ?buyer=<that Workspace ID>.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
DELETE /v1/workspaces/{regionId}/invoices/{invoiceId}/comments/{commentId}
Request
curl -X DELETE "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/comments/{commentId}" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
{
   "id": "string",
   "documentKind": "invoice" | "purchase_order",
   "documentId": "string",
   "body": "string",
   "visibility": "internal" | "shared",
   "authorSide": "buyer" | "supplier",
   "authorKey": "string",
   "authorName": "string",
   "authorOrgName": "string",
   "mentions": [ { "employeeKey": "string", "name": "string" } ],
   "createdAt": "string",
   "deletedAt": "string",
   "viewerIsAuthor": boolean
}

Submit invoice

Records a supplier's invoice document, in JSON, cXML or UBL.

POST/v1/workspaces/{regionId}/invoices

Body

  • invoiceNumberstring

    Supplier-provided invoice number, used to dedupe retried submissions within the region.

  • poIdstring

    Pleaxy PO id this invoice fulfills, e.g. "PO-AB12CD34". Omit to submit a standalone invoice.

  • supplierstringRequired

    Supplier / counterparty display name.

  • amountnumberRequired

    Invoice total. Must be greater than 0.

  • currencystring

    ISO 4217 code (here, or as UBL's currencyID / cXML's Money currency). Must be the region currency, and an invoice against a PO must be in the PO's currency. Pleaxy doesn't convert currencies.

  • dueInDaysnumber

    Payment terms in days. Defaults to the default payment terms of the contract the PO falls under, else 30.

  • lineItemsArray<{ description, qty?, unitPrice?, amount }>

    Optional line-item breakdown of the invoice.

  • lineItems[].ai{ invoicer, vendor, model, meter, region?, period? }

    An AI line: what it bills and the month of usage (YYYY-MM; the month before invoiceDate when left out), with qty in the meter's unit (millions of tokens, hours). Each AI line is matched to the order's AI line with the same scope and held to the contract term, the rate card and the provider's usage report; what it breaks shows as ai_* findings.

  • poLineNonumber

    With poId: the PO line this invoice bills, as in CreateInvoice. XML documents state it per line instead (UBL cac:OrderLineReference/cbc:LineID, cXML InvoiceDetailItemReference@lineNumber).

  • billsNoPoLineboolean

    With poId: true when the invoice bills none of the PO's lines, as in CreateInvoice. Not with poLineNo.

  • remittanceInstructionsobject

    Where the buyer should pay, stored with the invoice and never changed afterwards: { method, payeeName, bankName?, iban?, accountNumber?, bic?, routingNumber?, mailingAddress?, paymentReference?, notes? }. method is bank_transfer (needs iban or accountNumber), wire (iban or accountNumber, plus bic or routingNumber), ach (accountNumber and a 9-digit ABA routingNumber), check (mailingAddress) or other (notes). Pleaxy never verifies it; the buyer is warned when the payee or account differs from this supplier's previous invoice.

  • pdfBase64string

    Base64-encoded PDF of the invoice.

Returns

201 Created with the object shown in the response.

More details

Buyer operation: records a supplier's invoice document directly, without a prior Pleaxy-created PO flip. A JSON body with a connectionId is CreateInvoice instead. The JSON body below (supplier and amount, no connectionId) is deprecated and accepted until v2. A Supplier caller gets 400 SUPPLIER_DIRECT_INVOICE_RETIRED - an invoice sent this way reached no buyer; suppliers use CreateInvoice or FlipPurchaseOrderToInvoice instead. Accepts three request formats, selected by Content-Type: application/json (a CreateInvoiceDto body), or application/xml / text/xml containing either a cXML InvoiceDetailRequest or a UBL 2.1 Invoice document (auto-detected from the parsed XML). If poId is given, it's validated against an existing PO in awaiting_fulfillment status and the invoice is "pending" when a live billing figure exists for the PO's provider, and otherwise "unverified" with unverifiedReason "no_billing_connection"; if omitted, the invoice starts "unverified" with unverifiedReason "no_purchase_order". An XML document gets the same discount, charge and tax mapping, negative-line refusal and totals reconciliation as SubmitFeedInvoice, and a failure is 400 INVOICE_DOCUMENT_INVALID. The JSON body is unchanged.

Buyer callers only: call with a signed-in session or a buyer workspace's scoped X-Api-Key (see API keys). Supplier callers always get 400. Use CreateInvoice instead.

Response fields

  • idstring

    Invoice identifier. "INV-<PO suffix>" when poId was given, otherwise a random "INV-XXXXXXXX".

  • matchStatusstring

    "pending" if poId was given and a live billing figure exists for its provider, "unverified" otherwise.

  • unverifiedReasonstring

    Set when matchStatus is "unverified": "no_purchase_order" or "no_billing_connection".

  • currencystring

    The currency the invoice is recorded in, uppercase.

  • sourcestring

    Always "direct" for this endpoint.

  • submittedFormatstring

    "json", "cxml", or "ubl", based on the request's Content-Type.

Errors

  • 400 Bad Request (SUPPLIER_DIRECT_INVOICE_RETIRED) - The caller is a Supplier. Returned for every content type and credential, before the body is parsed as an invoice or validated, and nothing is stored. The request size limit still applies first, and a body that isn't valid JSON gets the generic 400 from the JSON parser. See ListInvoiceCounterparties and CreateInvoice.
  • 400 Bad Request - amount is not greater than zero, the JSON body fails validation, the referenced PO isn't in awaiting_fulfillment status, the XML body isn't recognizable cXML/UBL or carries a DOCTYPE or entity declaration (INVOICE_DOCUMENT_INVALID; cXML's standard external DTD reference is allowed), or the invoice's currency can't be taken - it isn't the region currency ("This invoice is in GBP, but Europe is invoiced in EUR. Ask the supplier to reissue it in EUR - Pleaxy doesn't convert currencies."), or it isn't the referenced PO's ("This invoice is in GBP, but PO-AB12CD34 is in EUR. Ask the supplier to reissue it in EUR - Pleaxy doesn't convert currencies."). The two currency refusals carry code INVOICE_CURRENCY_MISMATCH and INVOICE_PO_CURRENCY_MISMATCH.
  • 404 Not Found - poId was given but no PO with that id exists in the workspace.
  • 409 Conflict - invoiceNumber was already used by another invoice in this workspace, or the referenced PO already has an invoice.
  • 415 Unsupported Media Type - The Content-Type header is anything other than application/json, application/xml, or text/xml.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or a Buyer caller's procurement role lacks 'create' access to invoices - only administrator and finance have it ("Your procurement role ('read-only') doesn't have 'create' access to 'invoices'"; an API key is also told to re-mint itself with a role that grants it). On a bare regionId, a caller holding both profiles is treated as the Buyer; use the .supplier Workspace ID to act as the Supplier.
POST /v1/workspaces/{regionId}/invoices
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/reg_01k5x8p9j3q7vn2mhb4tzc6rde.buyer/invoices" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "supplier": "Corvo Cloud Services",
    "amount": 4820.00,
    "currency": "USD",
    "dueInDays": 30,
    "invoiceNumber": "CORVO-2026-0091"
  }'
Response · 201 Created
{
  "id": "INV-7F3A9C21",
  "supplier": "Corvo Cloud Services",
  "amount": 4820,
  "currency": "USD",
  "matchStatus": "unverified",
  "paid": false,
  "dueInDays": 30,
  "createdAt": "2026-02-03T09:12:44.000Z",
  "source": "direct",
  "submittedFormat": "json",
  "externalInvoiceNumber": "CORVO-2026-0091"
}

List invoice counterparties

Lists who you can invoice, or be invoiced by.

GET/v1/workspaces/{regionId}/invoices/counterparties

Returns

200 OK with the object shown in the response.

More details

The active counterparties the caller can invoice through CreateInvoice: for a Buyer caller its approved suppliers, for a Supplier caller the buyers that approved it. Each entry carries the connection id to pass as connectionId, the buyer's currency, and the open purchase orders between exactly that pair.

Signed-in session only. An API key gets 403.

Response fields

  • connectionIdstring

    The supplier connection to invoice - CreateInvoice's connectionId.

  • namestring

    The counterparty's name: the supplier's for a Buyer caller, the buyer's for a Supplier caller.

  • currencystring

    The buyer's currency. Every invoice to this counterparty is recorded in it.

  • openPurchaseOrdersArray

    Purchase orders awaiting fulfillment with an amount left to invoice, between exactly this buyer and supplier - the poIds CreateInvoice accepts. invoicedAmount is what earlier invoices drew, before tax (absent when none has). lines are the PO's lines an invoice can bill (its poLineNo): each one's amount and what earlier invoices drew of it (consumed), before tax. defaultDueInDays is the default payment terms of the contract the PO falls under, to prefill an invoice's dueInDays (absent when it has none). billToCompanyName is the company the PO's supplier invoices (its billToParty; absent on older POs). paidDeposits are paid invoices on the PO that an invoice can still deduct as a prepayment (deductions[].sourceInvoiceId): what each was owed and how much no invoice deducts yet, including tax (absent when none).

  • invoiceFeedEnabledboolean

    Buyer callers only: true when this supplier's automated invoices are on (see SetInvoiceFeed). On from approval; absent means off.

  • invoiceFeedUpdatedAtstring

    Buyer callers only: when automated invoices were last turned on or off. Absent if never.

  • feedReadinessstring

    Buyer callers with ?perConnection=true only: where this connection's automated invoices stand - "off" (the switch), "untested" (nothing has passed yet), "tested" (a ValidateFeedInvoice call passed), "live" (at least one invoice accepted). The supplier's own view (ListSupplierConnections) can also read "no_key" and "not_connected".

  • feedAcceptedCount, feedLastAcceptedAt, feedValidCount, feedLastValidAtnumber, string, number, string

    Buyer callers with ?perConnection=true only: the attempt log's mirror (ListFeedAttempts), once the feed has been called.

Errors

  • 403 Forbidden - The caller authenticated with an API key rather than a signed-in session ("This action requires a logged-in user session, not an API key"), its credential doesn't grant access to this workspace, or a Buyer caller's procurement role can't create invoices.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
GET /v1/workspaces/{regionId}/invoices/counterparties
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/counterparties" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
{
   "counterparties": [
      {
         "connectionId": "string",
         "name": "string",
         "currency": "string",
         "openPurchaseOrders": [
            {
               "id": "string", "amount": number, "createdAt": "string", "label": "string", "invoicedAmount": number, "defaultDueInDays": number, "billToCompanyName": "string",
               "lines": [ { "lineNo": number, "name": "string", "amount": number, "consumed": number } ],
               "paidDeposits": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "currency": "string", "owed": number, "available": number, "paidOn": "string", "prepayment": true, "reference": "string" } ]
            }
         ],
         "invoiceFeedEnabled": boolean,
         "invoiceFeedUpdatedAt": "string"
      }
   ]
}

Create invoice

Creates an invoice for a connected buyer or supplier.

POST/v1/workspaces/{regionId}/invoices

Body

  • documentTypestring

    invoice (the default), debit_note or credit_note. With credit_note the rest of the body is CreateCreditNote's (an unknown field is a 400) and the response is the credit note. With debit_note it's an invoice that amends another upward: give amendsInvoiceId (or amendsInvoiceNumber, the supplier's number); connectionId may be left out. It takes the original's counterparty, PO, PO line and currency (another PO or currency is 400 DEBIT_NOTE_INVALID), draws PO balance, and is approved and paid like any invoice. It is matched against the PO, except on a cloud provider's PO, where it starts unverified with unverifiedReason "debit_note" for a person to review. The original must be this supplier's invoice to this buyer (else 404 DEBIT_NOTE_ORIGINAL_NOT_FOUND), not rejected and not itself a debit note. Anything else is 400 DOCUMENT_TYPE_INVALID.

  • amendsInvoiceIdstring

    Debit note only: the Pleaxy id of the invoice it amends.

  • amendsInvoiceNumberstring

    Debit note only: the supplier's number of the invoice it amends, when the Pleaxy id isn't known.

  • replacesInvoiceIdsArray<string>

    A replacement invoice (credit and rebill: the supplier credits invoices in full and bills them again): the Pleaxy ids of the invoices and debit notes it replaces, 1–20, all from this supplier. It may bill another purchase order or currency than they do. Each original then lists it in replacedByInvoiceIds. An unknown invoice or another supplier's is 404 REPLACES_NOT_FOUND; a credit note, or replacements on a debit note, are 400 REPLACES_INVALID.

  • replacesInvoiceNumbersArray<string>

    A replacement invoice: the supplier's numbers of the invoices it replaces, when the Pleaxy ids aren't known. 1–20.

  • connectionIdstringRequired

    The counterparty to invoice - a connectionId from ListInvoiceCounterparties.

  • poIdstring

    An open purchase order between exactly this buyer and supplier, from the counterparty's openPurchaseOrders.

  • invoiceNumberstringRequired

    1–64 characters: letters, digits, '.', '_', '/' and '-', starting with a letter or digit. Unique per supplier within the buyer's workspace, case-insensitively.

  • invoiceDatestringRequired

    YYYY-MM-DD. Not in the future and not more than 365 days back.

  • dueInDaysnumber

    Payment terms in days, 0–365. Defaults to the default payment terms of the contract the PO falls under (ListInvoiceCounterparties' defaultDueInDays), else 30.

  • lineItemsArray<{ description, qty, unitPrice, discount?, charge? }>Required

    1–100 lines. description is 1–200 characters, qty is greater than 0 and at most 1,000,000, unitPrice is 0 to 1,000,000,000. A reduction is a discount, never a negative price: a negative unitPrice is 400 ("lineItems[1].unitPrice can't be negative. Send a reduction as that line's "discount", or as an "adjustments" entry with "type": "discount"."). discount and charge are optional totals for the line, 0 or more with no more decimal places than the buyer's currency allows (2 for USD and EUR, none for JPY: otherwise 400, e.g. "taxAmount must be a whole number in JPY."), and the line's discount can't be more than qty × unitPrice + charge. null isn't accepted: omit a field that doesn't apply. The total after discounts must be greater than 0, and the total at most 1,000,000,000,000,000. The PO line the invoice bills is stated once, on the invoice (poLineNo or billsNoPoLine), never on its lines: a line's own poLineNo or billsNoPoLine is ignored. Put shipping and handling in adjustments rather than as a line.

  • lineItems[].ai{ invoicer, vendor, model, meter, region?, period? }

    An AI line: what it bills and the month of usage (YYYY-MM; the month before invoiceDate when left out), with qty in the meter's unit (millions of tokens, hours). Each AI line is matched to the order's AI line with the same scope and held to the contract term, the rate card and the provider's usage report; what it breaks shows as ai_* findings.

  • poLineNonumber

    With poId: the PO line this invoice bills, by its lineNo (from GetPurchaseOrder or ListInvoiceCounterparties' openPurchaseOrders[].lines). An invoice bills one PO line, and all of its lines are held to it; to bill several PO lines, send an invoice for each. Lines are never matched to PO lines by description. Against a one-line PO, an invoice without one bills that line. Against a multi-line PO, an invoice naming none is a "discrepancy" (po_line_unassigned) that a person must resolve, because no line's limit or price could be checked. A lineNo the PO doesn't have is 400 INVOICE_PO_LINE_UNKNOWN. Not with billsNoPoLine (400 INVOICE_PO_LINE_CONTRADICTORY). A debit note without one takes its original's.

  • billsNoPoLineboolean

    With poId: true when the invoice bills none of the PO's lines, such as an unordered extra or a one-off fee. It draws only on the PO's balance, and the invoice is never approved automatically. Not with poLineNo.

  • adjustmentsArray<{ type, description, amount }>

    Invoice-level discounts and charges, before tax. At most 20. type is "discount" or "charge"; description is 1–200 characters, stored without control characters, line breaks or bidirectional-text controls; amount is a positive magnitude, 0.01 or more with no more decimal places than the buyer's currency allows (type gives the sign).

  • taxAmountnumber

    Total tax on the invoice, as stated by the supplier. It is included in the invoice total. Pleaxy doesn't calculate tax. 0 or more, with no more decimal places than the buyer's currency allows. Omit it when the invoice states no tax; null is refused.

  • currencystring

    ISO 4217, optional. Checked only: it must be the buyer's currency, which is always the one stored.

  • pdfBase64string

    Base64-encoded PDF of the invoice, up to 8MB.

  • remittanceInstructionsobject

    Where the buyer should pay, stored with the invoice and never changed afterwards: { method, payeeName, bankName?, iban?, accountNumber?, bic?, routingNumber?, mailingAddress?, paymentReference?, notes? }. method is bank_transfer (needs iban or accountNumber), wire (iban or accountNumber, plus bic or routingNumber), ach (accountNumber and a 9-digit ABA routingNumber), check (mailingAddress) or other (notes). Pleaxy never verifies it; the buyer is warned when the payee or account differs from this supplier's previous invoice.

  • withholdingsArray<{ description, amount, rate? }>

    Optional. Tax the buyer withholds from what it pays (retenciones, e.g. ISR retenido), in the invoice's currency. They lower the amount due, never the total or its tax. With the deductions, less than the total. At most 20.

  • deductionsArray<{ kind, description, amount, reference?, paidOn?, sourceInvoiceId? }>

    Optional. What was already paid towards the invoice after tax: kind "prepayment" for a deposit or advance the buyer paid (it needs a reference, or sourceInvoiceId naming your paid invoice on the same PO, whose payment reference and date are then filled in and which the PO draws only once), or kind "credit" for a credit you applied on the invoice. They lower the amount due, never the total or its tax. With the withholdings, less than the total; not with paymentCurrencyAmount. At most 20. UBL: cbc:PrepaidAmount; cXML: DepositAmount.

  • taxBreakdownArray<{ description, amount, rate? }>

    Optional. Tax by type in the invoice's currency, e.g. [{ "description": "IVA", "rate": 21, "amount": 199.50 }, { "description": "Percepción IIBB", "amount": 28.50 }]. rate is a percentage and optional. Must sum to taxAmount, which is then required (400 TAX_BREAKDOWN_MISMATCH). At most 20. Display only. UBL: the TaxSubtotals of the TaxTotal in the document currency, kept when they add up.

  • paymentCurrencyAmountobject

    Optional. Only when the invoice names the amount owed in another currency (e.g. AWS billing an Argentine customer in ARS); then totalAmount is the amount due, which payables, payments and the payment record use: { currencyCode, totalAmount, totalAmountBeforeTax?, amountBreakdown?: { subTotalAmount?, discounts?, fees?, taxes? }, currencyExchangeDetails: { rate, rateDate?, rateSource?, sourceCurrencyCode?, targetCurrencyCode? } }, the same shape as AWS Invoicing's PaymentCurrencyAmount. rate is 1 invoice currency = rate currencyCode, as printed on the invoice. Leave it off when the invoice is converted at the payment-date rate. Each stated figure must be within 0.5% (or one minor unit) of the invoice's figure × rate (400 STATED_AMOUNT_RATE_MISMATCH, naming both figures), each breakdown must add up to its total, and currencyCode can't be the invoice's own (400 SAME_CURRENCY). rateDate is YYYY-MM-DD, not in the future and at most a day after the invoice date. UBL: cbc:PaymentCurrencyCode with cac:PaymentExchangeRate; cXML: Money@alternateCurrency/alternateAmount on DueAmount. Recorded as stated; Pleaxy doesn't convert anything.

  • taxCurrencyAmountobject

    Optional. Tax in the legal currency of the buyer's country when that isn't the invoice's currency. Same shape as paymentCurrencyAmount; amountBreakdown.taxes.totalAmount is required and totalAmount is optional. Needs taxAmount. UBL: cbc:TaxCurrencyCode's cac:TaxTotal with cac:TaxExchangeRate, or without it (EN 16931 BT-111) a rate derived from the two tax totals; cXML: Money@alternateAmount on Tax. A derived rate has rateSource "derived" and isn't checked against the amounts it came from. Display only.

Returns

201 Created with the object shown in the response.

More details

Sent as an application/json body with a connectionId; the same path without one is SubmitInvoice. This replaced POST /v1/workspaces/{regionId}/invoices/manual, which now returns 410 ROUTE_MERGED. Records an invoice for one active counterparty from ListInvoiceCounterparties - a Supplier invoicing one of its buyers, or a Buyer entering one of its suppliers' invoices. The invoice is stored in the buyer's workspace, in the buyer's currency, so both sides see it. The server computes each line's amount as qty × unitPrice + charge − discount, and the total as the lines, minus invoice-level discounts, plus invoice-level charges, plus taxAmount. Each computed figure is rounded half up to the buyer currency's minor unit: to the cent for USD and EUR, to whole units for JPY (3 × 333.33 JPY is a line amount of 1,000). That total is the amount matched, approved, capped and paid. With poId it is "pending" when a live billing figure exists for the PO's provider, and otherwise "unverified" with unverifiedReason "no_billing_connection" (buyer view only); without poId it is "unverified" with unverifiedReason "no_purchase_order". A Buyer who enters an invoice can't approve, resolve, export or record payment for it unless nobody else in the workspace could, and paying it through Stripe always takes someone else.

Buyer callers: a signed-in session or a buyer workspace's X-Api-Key. Whoever recorded it, and for a key also the person who created the key, can't approve, resolve, export or record payment for it, unless a signed-in person is the only one in the workspace who could; paying it through Stripe always takes someone else. Supplier callers: signed-in session only; a supplier API key gets 400 SUPPLIER_DIRECT_INVOICE_RETIRED.

Response fields

  • sourcestring

    Always "manual" for this endpoint.

  • amountnumber

    The payable total: the lines after their own discounts and charges, minus invoice-level discounts, plus invoice-level charges, plus taxAmount. Matching, approval, autonomy, contract caps and settlement all use it.

  • lineItemsArray

    Each line as stored. amount is the net line, before tax; discount and charge appear only on a line that has them.

  • adjustmentsArray

    Invoice-level discounts and charges. Absent when there are none.

  • taxAmountnumber

    The stated tax, already included in amount. Display only: no decision reads it. Absent when none was stated.

  • currencystring

    The buyer's currency.

Errors

  • 400 Bad Request - The body fails validation (including more than 20 adjustments, a negative taxAmount, or more than 2 decimal places on a discount, charge, adjustment or taxAmount), INVOICE_DOCUMENT_INVALID (a negative unitPrice; a line discount larger than its line: "Line 2's discount (12.00 USD) is more than the line (10.00 USD). Check lineItems[1].discount."; a total after discounts that isn't greater than 0: "The invoice total after discounts must be greater than zero."; a total over 1,000,000,000,000,000; or more decimal places than the buyer's currency allows on a discount, charge, adjustment or taxAmount ("lineItems[0].discount must be a whole number in JPY.")), invoiceDate is out of range (INVOICE_DATE_OUT_OF_RANGE), the counterparty isn't connected yet (COUNTERPARTY_NOT_ACTIVE), poId can't be invoiced (PO_NOT_INVOICEABLE), poLineNo is a line the PO doesn't have (INVOICE_PO_LINE_UNKNOWN) or comes with billsNoPoLine (INVOICE_PO_LINE_CONTRADICTORY), or currency isn't the buyer's (INVOICE_CURRENCY_MISMATCH).
  • 404 Not Found - No counterparty with this connectionId is visible to the caller.
  • 409 Conflict - This supplier already has an invoice with this number (INVOICE_NUMBER_TAKEN).
  • 400 Bad Request - The body has an amount (AMOUNT_NOT_ACCEPTED: "Leave amount off: Pleaxy computes the total from the line items."), or a Supplier called with an API key (SUPPLIER_DIRECT_INVOICE_RETIRED).
  • 403 Forbidden - The credential doesn't grant access to this workspace, or a Buyer caller's procurement role can't create invoices.
  • 422 Unprocessable Entity - The counterparty is the caller's own buyer or supplier profile (SELF_DEALING).
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
POST /v1/workspaces/{regionId}/invoices
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "connectionId": "string",
   "poId": "string",
   "invoiceNumber": "string",
   "invoiceDate": "string",
   "dueInDays": number,
   "poLineNo": number,
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "currency": "string",
   "pdfBase64": "string"
}
Response · 201 Created
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Get exchange rates

Returns the exchange rates used to compare amounts with your limits.

GET/v1/workspaces/{regionId}/exchange-rates

Returns

200 OK with the object shown in the response.

More details

The rates Pleaxy uses only to compare orders and invoices in other currencies with your approval thresholds, amount conditions, auto-approval limits, automation rules and spend limits. Amounts, invoices and payments are never converted. One row per currency your orders, invoices and suppliers use: the reference rate into your workspace's currency (a daily rate from Exchange Rate API, used while it is at most 3 days old). Workspaces can't set their own rates. With no fresh rate, a document in that currency goes through every approval step and is never auto-approved. A chain built with a rate records it as approval.fx: { from, to, rate, source: provider | none, asOf, controlAmount, supplierStated? }. When an invoice states its own figure in your workspace's currency (its paymentCurrencyAmount, or its total at its taxCurrencyAmount rate), controlAmount is the larger of that figure and the figure at the reference rate, so a low stated rate can't lower the routing; supplierStated { amount, rateSource, rateDate } shows the invoice's figure. With no fresh reference rate, the chain still goes through every approval step, never to the invoice's figure. An invoice whose stated rate is more than warningPct from the reference rate when it arrives carries rateWarnings (buyer view only); they never block it. Rates By Exchange Rate API (https://www.exchangerate-api.com).

Signed-in session only, with 'view' access to approval settings. An API key gets 403 HUMAN_SESSION_REQUIRED: the rate provider's terms don't allow its rates to be offered through an API.

Errors

  • 403 Forbidden - An API key (HUMAN_SESSION_REQUIRED), a Supplier caller, or a procurement role without 'view' on approval settings.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
GET /v1/workspaces/{regionId}/exchange-rates
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/exchange-rates" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
{
   "workspaceCurrency": "string",
   "warningPct": number,
   "rows": [
      { "currency": "string", "providerRate": number, "providerAsOf": "string" }
   ],
   "provider": { "asOf": "string", "fresh": boolean, "attribution": { "text": "string", "url": "string" } }
}

Set exchange rate warning

Sets when an invoice's own exchange rate gets a warning.

PUT/v1/workspaces/{regionId}/exchange-rates

Body

  • warningPctnumberRequired

    0 to 100.

Returns

200 OK with the object shown in the response.

More details

Sets how far, in percent, an invoice's own stated rate (its paymentCurrencyAmount or taxCurrencyAmount rate) may be from the reference rate (a fresh daily rate from Exchange Rate API) before the invoice carries rateWarnings. Default 5. Checked when an invoice arrives; invoices already received keep their warnings. A warning never blocks an invoice. With no fresh reference rate there's nothing to compare and no warning.

Signed-in session only (an API key gets 403 HUMAN_SESSION_REQUIRED), with 'edit' access to approval settings: administrators.

Errors

  • 400 Bad Request - warningPct missing, or not a number from 0 to 100.
  • 403 Forbidden - An API key, a Supplier caller, or a procurement role without 'edit' on approval settings.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
PUT /v1/workspaces/{regionId}/exchange-rates
Request
curl -X PUT "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/exchange-rates" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "warningPct": number
}
Response · 200 OK
{
   "warningPct": number
}

Get reference rate

Returns the reference exchange rate for a date.

GET/v1/workspaces/{regionId}/reference-rate

Parameters

  • fromstringRequired

    ISO 4217 code: the invoice's currency.

  • tostringRequired

    ISO 4217 code: the payment or tax currency.

  • datestring

    YYYY-MM-DD, the invoice date. Default today.

Returns

200 OK with the object shown in the response.

More details

The reference rate for 1 from in to on an invoice date: Exchange Rate API's daily rate for that date, or the latest of the 3 days before it, never a later day's. With no date, today's, while it is at most 3 days old. The New invoice form uses it to suggest the rate for a payment or tax currency; the invoice records whatever rate and figures are submitted, and Pleaxy checks they agree. reference is absent when there's no rate (same currency, an unknown currency, or no snapshot for that date). Rates By Exchange Rate API (https://www.exchangerate-api.com).

Signed-in session only, with 'create' access to invoices. An API key gets 403 HUMAN_SESSION_REQUIRED: the rate provider's terms don't allow its rates to be offered through an API.

Errors

  • 400 Bad Request - from or to isn't a 3-letter ISO 4217 code, or date isn't YYYY-MM-DD.
  • 403 Forbidden - An API key (HUMAN_SESSION_REQUIRED), or a role without 'create' on invoices.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
GET /v1/workspaces/{regionId}/reference-rate
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/reference-rate" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
{
   "reference": {
      "from": "string",
      "to": "string",
      "rate": number,
      "date": "string",
      "source": "string",
      "attribution": { "text": "string", "url": "string" }
   }
}

Set supplier currencies

Sets the currencies a supplier bills in and is paid in.

PUT/v1/workspaces/{regionId}/supplier-connections/{connectionId}/currencies

Parameters

  • connectionIdstringRequired

    The supplier connection.

Body

  • billingCurrencystring | null

    ISO 4217. Only for a supplier with no Pleaxy account; one with an account sets its own currencies.

  • paymentCurrencystring | null

    ISO 4217. For a supplier with an account, one it accepts payment in.

Returns

200 OK with the object shown in the response.

More details

Sets two defaults for one supplier: "Bills in" (billingCurrency), the currency a supplier with no Pleaxy account invoices you in, used for its new orders and invoices; and "Pay in" (paymentCurrency), the currency you usually pay it in, suggested as a new invoice's payment currency. Absent leaves a field, null removes it. Existing orders and invoices keep their currencies. The supplier never sees either.

Signed-in session or a buyer workspace's X-Api-Key, with 'edit' access to suppliers: administrator or contracts-and-suppliers.

Errors

  • 400 Bad Request - A code that isn't ISO 4217, billingCurrency for a supplier with an account (BILLING_CURRENCY_SUPPLIER_OWNED), or a paymentCurrency it doesn't accept (PAYMENT_CURRENCY_NOT_ACCEPTED).
  • 403 Forbidden - A Supplier caller, or a procurement role without 'edit' on suppliers.
  • 404 Not Found - No such connection in this workspace.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
PUT /v1/workspaces/{regionId}/supplier-connections/{connectionId}/currencies
Request
curl -X PUT "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/supplier-connections/{connectionId}/currencies" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "billingCurrency": "string" | null,
   "paymentCurrency": "string" | null
}
Response · 200 OK
{
   "id": "string",
   "supplierName": "string",
   "billingCurrency": "string",
   "paymentCurrency": "string"
}

Set invoice feed

Turns a supplier's automated invoices on or off.

PUT/v1/workspaces/{regionId}/supplier-connections/{connectionId}/invoice-feed

Parameters

  • connectionIdstringRequired

    The supplier connection.

Body

  • enabledbooleanRequired

    true to accept this supplier's automated invoices, false to stop.

Returns

200 OK with the object shown in the response.

More details

Turns a supplier's automated invoices (SubmitFeedInvoice) on or off for one approved connection. On from the moment you approve the supplier's onboarding (connections approved before 9 October 2026 keep the value they had); this is the stop button. Turning it off refuses the supplier's very next request; invoices already received stay in Invoices & matching and keep moving through approval. The supplier is notified (invoice_feed_changed). Setting the value it already has changes nothing. Returns the connection with invoiceFeedEnabled, invoiceFeedUpdatedAt and invoiceFeedUpdatedBy. ListInvoiceCounterparties shows each connection's current setting and readiness.

Signed-in session only (an API key gets 403), with 'edit' access to invoices: administrator or finance.

Errors

  • 400 Bad Request - The connection isn't approved yet (COUNTERPARTY_NOT_ACTIVE), or enabled isn't a boolean.
  • 403 Forbidden - An API key rather than a signed-in session, a Supplier caller, or a procurement role without 'edit' on invoices (read-only, contracts-and-suppliers, relationships, requisitions).
  • 404 Not Found - No connection with this id in the workspace.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
PUT /v1/workspaces/{regionId}/supplier-connections/{connectionId}/invoice-feed
Request
curl -X PUT "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/supplier-connections/{connectionId}/invoice-feed" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "enabled": boolean
}
Response · 200 OK
{
   "id": "string",
   "supplierName": "string",
   "status": "approved",
   "invoiceFeedEnabled": boolean,
   "invoiceFeedUpdatedAt": "string",
   "invoiceFeedUpdatedBy": "string"
}

List feed attempts

Lists the automated invoice attempts on a connection.

GET/v1/workspaces/{regionId}/supplier-connections/{connectionId}/feed-attempts

Parameters

  • connectionIdstringRequired

    The supplier connection.

  • beforestring

    The cursor of the last row of the page you hold, for the page after it. 50 rows a page.

Returns

200 OK with the object shown in the response.

More details

The attempt log of one connection's automated invoices: every call to SubmitFeedInvoice or ValidateFeedInvoice that reached the connection, newest first, with what Pleaxy answered. A refusal before the connection was known (no key, wrong scope, the key's rate limit, a connection that isn't the caller's) can't be attributed to one and isn't logged. The document itself is never stored. The supplier reads its own connections; the buyer reads the suppliers it approved; both see the same rows. Rows are kept 90 days. The connection itself (ListSupplierConnections, ListInvoiceCounterparties with ?perConnection=true) mirrors the last of each kind: feedLastAttemptAt, feedLastAcceptedAt, feedAcceptedCount, feedLastRefusedAt, feedLastRefusedCode, feedLastValidAt, feedValidCount.

Signed-in session or API key. A Buyer caller needs 'view' access to suppliers.

Response fields

  • modestring

    "submit" for the live route, "validate" for the test one.

  • formatstring

    Known once the body was parsed; absent when the call was refused before that.

  • outcomestring

    "accepted" (201, an invoice created), "replayed" (200, the same document again), "valid" (a passed test), "refused" (any error after the connection was known, including a feed turned off).

  • status, code, messagenumber, string, string

    The response the caller got: the HTTP status and, on a refusal, its code and message as returned.

  • messageId, paramsstring, object

    On a refusal, the error's messageId and params as returned, to show it in your reader's language. Absent on rows logged before they were kept.

  • invoiceNumberstring

    Your invoice number, when parsing got that far.

  • invoiceIdstring

    The invoice created or replayed; on a passed test, the invoice a replayed number already names.

  • creditNoteIdstring

    The credit note created or replayed, when the document was a credit note; invoiceNumber is then its number.

  • keyIdstring

    The API key's id (never its secret), to match against ListApiKeys.

  • beforestring

    The last row's cursor when there may be older rows; absent on the last page.

Errors

  • 400 Bad Request - before isn't a cursor from a previous page.
  • 403 Forbidden - A Buyer caller's procurement role can't view suppliers (requisitions).
  • 404 Not Found - No connection with this id is yours.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
GET /v1/workspaces/{regionId}/supplier-connections/{connectionId}/feed-attempts
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/reg_01k5x8p9j3q7vn2mhb4tzc6rde.supplier/supplier-connections/SC-1A2B3C4D/feed-attempts" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
{
  "attempts": [
    {
      "at": "2026-10-09T10:12:44.000Z",
      "mode": "submit",
      "format": "ubl",
      "outcome": "refused",
      "status": 400,
      "code": "INVOICE_CURRENCY_MISMATCH",
      "message": "This invoice is in EUR, but Beta Buyer is invoiced in USD. Reissue it in USD - Pleaxy doesn't convert currencies.",
      "invoiceNumber": "INV-2026-0143",
      "keyId": "k_01k6…",
      "cursor": "FEEDATTEMPT#2026-10-09T10:12:44.000Z#3f9a1c2e"
    },
    {
      "at": "2026-10-09T09:58:02.000Z",
      "mode": "validate",
      "format": "json",
      "outcome": "valid",
      "status": 200,
      "invoiceNumber": "INV-2026-0001",
      "keyId": "k_01k6…",
      "cursor": "FEEDATTEMPT#2026-10-09T09:58:02.000Z#b71e0d44"
    }
  ]
}

Run invoice match

Compares an invoice with metered cloud usage.

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/run-match

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Returns

201 Created with the object shown in the response.

More details

Runs 3-way matching: compares the invoiced amount (amount) against metered usage (usageAmount, in usageCurrency - the provider's own currency, from a live billing connection). Only a real figure is compared: an older pending invoice with no real usage figure becomes "unverified" (unverifiedReason "no_billing_connection") instead, with no approval chain. Only a figure from an issued AWS invoice lets autonomy or an automation rule approve it with nobody acting; a match against any other source is sent to a person. Within tolerance (3%) the invoice becomes matched and an approval chain is built. Outside tolerance, it auto-resolves to matched if the overage qualifies for autonomy, otherwise it becomes discrepancy (discrepancyReason "amount") and waits for ResolveInvoiceDiscrepancy. Pleaxy doesn't convert currencies: when usage is in another currency than the invoice, nothing is compared and no autonomy or automation rule applies - the invoice becomes discrepancy with discrepancyReason "currency" and needs ResolveInvoiceDiscrepancy. An invoice against a purchase order that isn't for a cloud provider has no usage to compare: it is created pending with matchBasis "purchase_order" when it passes its PO and contract checks, and this builds its approval chain - matched, always with a human approver, never autonomy or an automation rule. One created "unverified" (unverifiedReason "no_billing_connection") before that is checked against its PO and contract again: matched, or discrepancy with discrepancyReason "controls". The approval chain's requester is the authenticated caller (an API key is recorded as apikey:<keyId>), never a name passed in; the request takes no body.

Response fields

  • matchStatusstring

    "matched" or "discrepancy" after running the match, or "unverified" for an older pending invoice with no real usage figure.

  • discrepancyReasonstring

    Set only on a discrepancy: "amount" (out of tolerance), "currency" (usage in another currency, never compared) or "controls" (breaks its PO or contract).

  • matchBasisstring

    "purchase_order" when the invoice was matched against a purchase order that isn't for a cloud provider rather than against usage. Absent on a usage match.

Errors

  • 400 Bad Request - The invoice's matchStatus isn't "pending" (or "unverified" against a purchase order that isn't for a cloud provider). For any other unverified invoice (no purchase order, or no billing connection): "Invoice '{invoiceId}' can't be matched: it's unverified, with no usage figure to compare against. Nothing was matched. A person must review it in Pleaxy before it can go for approval." Otherwise: "Invoice '{invoiceId}' has already been matched (result: {matchStatus})."
  • 404 Not Found - No invoice with this id exists in the workspace.
  • 409 Conflict - Another run or decision landed concurrently; re-read the invoice.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/run-match
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/run-match" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 201 Created
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Resolve invoice discrepancy

Sends a reviewed invoice with a discrepancy to approval.

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/resolve-discrepancy

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Returns

201 Created with the object shown in the response.

More details

Manual override for an invoice a human has reviewed - starts a normal (non-autonomy) approval chain for a discrepancy or unverified invoice. A discrepancy becomes "matched"; an unverified invoice stays "unverified" with its pending chain, because nothing was checked. The approval chain's requester is the authenticated caller (an API key is recorded as apikey:<keyId>), never a name passed in; the request takes no body.

An API key can resolve an amount or currency discrepancy. Only a signed-in session can resolve an unverified invoice, for either unverifiedReason; an API key gets 403 HUMAN_REVIEW_REQUIRED.

Errors

  • 400 Bad Request - The invoice's matchStatus is neither "discrepancy" nor "unverified".
  • 403 Forbidden - HUMAN_REVIEW_REQUIRED: the invoice is unverified and the caller is an API key ("An unverified invoice has nothing to match it against, so a person must review it. Sign in to Pleaxy to resolve it."). Nothing is changed.
  • 404 Not Found - No invoice with this id exists in the workspace.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/resolve-discrepancy
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/resolve-discrepancy" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 201 Created
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Approve invoice

Approves an invoice's current approval step.

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/approve

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Returns

201 Created with the object shown in the response.

More details

Advances the invoice's approval chain by one stage. Only the employee the current step names may call this - no role, permission or administrator rights substitute for being named, and an API key can never be named. A completed approval sends an invoice_approved notification to the supplier. It takes no body and acts as the authenticated caller (an API key acts as apikey:<keyId>).

Signed-in session only. Any other caller, including an API key, gets 403 HUMAN_APPROVAL_REQUIRED before anything is read.

Errors

  • 400 Bad Request - The invoice has no pending approval stage.
  • 403 Forbidden - Code HUMAN_APPROVAL_REQUIRED. The caller is an API key. Approval steps name a specific employee, and a key can never be one, so a key can neither approve nor reject a step. Ask the employee the step names to act on it in Pleaxy.
  • 403 Forbidden - Code APPROVER_NOT_ASSIGNED. The current step has no approver assigned, so nobody can act on it. An administrator must assign one with ReassignPurchaseOrderApprovalStage / ReassignInvoiceApprovalStage first.
  • 403 Forbidden - The caller is not the employee the current step names. Only that employee may approve or reject it - no role or permission substitutes for being named.
  • 404 Not Found - No invoice with this id exists in the workspace.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/approve
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/approve" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 201 Created
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Reject invoice

Rejects an invoice, with a reason the supplier sees.

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/reject

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Body

  • reasonCodestringRequired

    One of amount_incorrect, not_ordered_or_not_received, duplicate, missing_or_wrong_po, wrong_billing_entity, other. The supplier sees its label.

  • reasonstringRequired

    The note the supplier sees: what they should fix. Line breaks become spaces, and control and invisible characters are removed. It then needs at least 10 letters or numbers and at most 500 characters. Plain text; never shown as a link. Don't include personal details or internal comments.

Returns

201 Created with the object shown in the response.

More details

Rejects the invoice at its current approval stage, with a reason the supplier sees. Only the employee the current step names may call this. An unverified or discrepancy invoice that has no approval chain yet is a separate case: any buyer with invoices:edit may reject it, except the buyer who entered it. Signed-in sessions only: an API key can't reject. The supplier sees the reason and the note word for word, but not who rejected it, and gets a notification naming the reason only. The rejection is stored on the invoice as `rejection`. Rejecting an invoice that holds its purchase order's claim reopens that purchase order in the same write, so the supplier can send a corrected invoice against it: it gets the id INV-<PO suffix>-R<n> and `replacesInvoiceId`, and needs a new invoice number, because the rejected invoice keeps its own.

Errors

  • 400 Bad Request - The invoice has no pending approval stage.
  • 400 Bad Request - Code INVOICE_REJECTION_REASON_INVALID. reasonCode is missing or not one of the six codes, or reason is missing, not a string, has fewer than 10 letters or numbers, or is longer than 500 characters once cleaned.
  • 403 Forbidden - Code HUMAN_APPROVAL_REQUIRED: "Rejecting needs you, signed in to Pleaxy." The caller is an API key. Rejecting is a decision the supplier sees, so it needs a signed-in person.
  • 403 Forbidden - Code APPROVER_NOT_ASSIGNED. The current step has no approver assigned, so nobody can act on it. An administrator must assign one with ReassignPurchaseOrderApprovalStage / ReassignInvoiceApprovalStage first.
  • 403 Forbidden - The caller is not the employee the current step names. Only that employee may approve or reject it - no role or permission substitutes for being named.
  • 404 Not Found - No invoice with this id exists in the workspace.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/reject
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/reject" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other",
   "reason": "string"
}
Response · 201 Created
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Reassign invoice approval stage

Assigns a pending approval step to another person.

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/approval/reassign

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Body

  • stageIdstringRequired

    Id of the pending step to reassign. It may sit in any group of the chain, not only the current one.

  • approverEmployeeKeystringRequired

    Employee key (Cognito sub) of the new approver. Must be a buyer member of this workspace, and not the acting administrator.

  • reasonstring

    Why the step was reassigned, up to 280 characters. Shown in the approval history.

Returns

201 Created with the object shown in the response.

More details

The invoice equivalent of ReassignPurchaseOrderApprovalStage: re-points one pending step of this invoice's approval chain at a different named employee. Administrator only (approvalSettings:edit), signed-in sessions only, document-scoped, and recorded in the approval history. This is how a supplier-linked invoice that failed closed onto an unassigned "Manual approval" step is unblocked.

Errors

  • 400 Bad Request - The chain is already approved or rejected; the step has already been approved or rejected; the new approver isn't an employee of this workspace; the new approver raised the order / entered the invoice, is separation-of-duties excluded, already cleared the spend limit with their own authority, or already holds another seat on the same step; or the step is the spend-limit review, whose approver is a frozen audit fact (change it in Settings › Spend limits).
  • 403 Forbidden - The caller lacks approvalSettings:edit (administrator), or named themselves as the new approver - assigning a live step to yourself makes one person the whole chain with no trace of policy. Code HUMAN_APPROVAL_REQUIRED when the caller is an API key: only a signed-in administrator can reassign an approval step.
  • 404 Not Found - No document with this id exists in the workspace, or the chain has no step with this stageId.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/approval/reassign
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/approval/reassign" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "stageId": "string",
   "approverEmployeeKey": "string",
   "reason": "string"
}
Response · 201 Created
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Record payment

Records that an invoice was paid outside Pleaxy.

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/payment-record

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Body

  • methodstringRequired

    How it was paid: ach, wire, check, card or other.

  • paymentDatestringRequired

    YYYY-MM-DD the payment was sent. Not later than today in the buyer workspace's time zone, and not before the invoice date.

  • referencestringRequired

    The bank or payment tool's reference, such as an ACH trace or check number, 1–64 characters once cleaned. The supplier sees it. Never a bank account number.

  • currencystring

    Only for a payment in another currency than the amount due's: ISO 4217. Needs amount and rate.

  • amountnumber

    With currency: what was paid, in currency. The amount due × rate, within 0.5% or one minor unit.

  • ratenumber

    With currency: 1 {amount due's currency} = rate {currency}, the rate the payment was made at.

  • rateDatestring

    With currency: YYYY-MM-DD the rate is for, not later than today. Defaults to paymentDate.

  • remittance{ note?, file?: { fileId, fileName } }

    A remittance for the supplier, sent with the payment's notice: a note, a proof of payment uploaded with RequestRemittanceUpload, or both. Same rules as SendRemittance.

  • confirmReplacedboolean

    True to record it anyway when the invoice is replaced (credit and rebill) and its credit notes don't cover it in full: paying it as well as its replacement pays twice.

Returns

201 Created with the object shown in the response.

More details

Records that an approved invoice was paid outside Pleaxy, with the method, date and reference. This moves no money and doesn't check that the payment happened. A payment covers the full amount due: the invoice's paymentCurrencyAmount.totalAmount when it states one, else its amount, in that currency. A partial payment can't be recorded. Paid in another currency than the amount due's? Send amount, currency and rate too: the amount must be the amount due × rate, within 0.5% or one minor unit, and is recorded as paidAmount; Pleaxy doesn't convert anything. The invoice reads as paid, and the supplier is notified that the buyer recorded the payment and gets a remittance advice marked as recorded by the buyer and not made through Pleaxy. Works on an exported invoice too, which completes its export. Recording the same details again answers 200 with alreadyRecorded: true and changes nothing. Optionally send the supplier a remittance with it (remittance), as SendRemittance does.

A signed-in session with 'edit' access to invoices. Changed 2026-09-24, a security fix inside v1: an API key can't record a payment (403 HUMAN_SESSION_REQUIRED). The person who entered the invoice, and members of the supplier's profile, can't record its payment.

Errors

  • 400 Bad Request - A field is missing or invalid (codes PAYMENT_METHOD_INVALID, PAYMENT_DATE_INVALID, PAYMENT_DATE_IN_FUTURE, PAYMENT_DATE_BEFORE_INVOICE, PAYMENT_REFERENCE_INVALID), the paid amount doesn't fit (PAID_AMOUNT_SAME_CURRENCY: currency is the amount due's own, so leave amount, currency and rate off; PAID_AMOUNT_INVALID: amount or rate missing or not above 0, or a bad rateDate; PAID_AMOUNT_RATE_MISMATCH: amount isn't the amount due × rate), the remittance is invalid (as for SendRemittance), or the body carries a field it doesn't accept.
  • 403 Forbidden - Code HUMAN_SESSION_REQUIRED: the caller is an API key. Or a separation-of-duties refusal: the caller entered the invoice or is a member of the supplier's profile.
  • 404 Not Found - No invoice with this id exists in the workspace.
  • 409 Conflict - Code PAYMENT_ALREADY_RECORDED (a different payment is already recorded: withdraw it first), STRIPE_PAYMENT_IN_PROGRESS (a payment through Pleaxy with Stripe was started), EXPORT_COOLING_OFF (its export was withdrawn; try again next business day), REPLACED_UNCREDITED (the invoice is replaced and its credit notes don't cover it in full: check with the supplier, then send confirmReplaced: true) or PAYMENT_CHANGED.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/payment-record
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/payment-record" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "method": "ach" | "wire" | "check" | "card" | "other",
   "paymentDate": "YYYY-MM-DD",
   "reference": "string",
   "currency": "string",
   "amount": number,
   "rate": number,
   "rateDate": "YYYY-MM-DD",
   "remittance": { "note": "string", "file": { "fileId": "string", "fileName": "string" } },
   "confirmReplaced": boolean
}
Response · 201 Created
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Withdraw payment record

Withdraws a recorded payment.

DELETE/v1/workspaces/{regionId}/invoices/{invoiceId}/payment-record

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Body

  • confirmbooleanRequired

    Must be true.

Returns

200 OK with the object shown in the response.

More details

Withdraws the buyer's record of a payment. The invoice reads as unpaid again and the supplier is told that the record was withdrawn. This doesn't cancel or reverse the payment itself. The remittance advice is then marked Withdrawn.

A signed-in session with 'edit' access to invoices; an API key gets 403 HUMAN_SESSION_REQUIRED.

Errors

  • 403 Forbidden - Code HUMAN_SESSION_REQUIRED, or a separation-of-duties refusal.
  • 404 Not Found - No invoice with this id exists, or code NO_PAYMENT_RECORD: no payment is recorded for it.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
DELETE /v1/workspaces/{regionId}/invoices/{invoiceId}/payment-record
Request
curl -X DELETE "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/payment-record" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "confirm": true
}
Response · 200 OK
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Export invoices for payment

Exports approved, unpaid invoices as a CSV for your payment run.

POST/v1/workspaces/{regionId}/invoices/payment-export

Body

  • invoiceIdsstring[]

    Optional: export only these invoices, 1 to 1,000 ids. Left out (or an empty body), every eligible invoice. Any other field is a 400.

  • confirmReplacedboolean

    With invoiceIds: export a replaced invoice (credit and rebill) its credit notes don't cover in full anyway. Without invoiceIds, such an invoice is always left out.

Returns

200 OK with the object shown in the response.

More details

Downloads approved, unpaid invoices as CSV for your own payment run, due date first, up to 1,000 - every eligible invoice, or only the ones you name in invoiceIds (an ineligible one is left out). Each exported invoice is locked until its payment is recorded or the export is withdrawn, so it can't be exported or recorded twice. The file has no bank details. The supplier doesn't see the export. Response headers: X-Pleaxy-Export-Id, X-Pleaxy-Export-Count, X-Pleaxy-Export-Truncated.

A signed-in session with 'edit' access to invoices; an API key gets 403 HUMAN_SESSION_REQUIRED.

Errors

  • 403 Forbidden - Code HUMAN_SESSION_REQUIRED.
  • 400 Bad Request - The body isn't empty, {} or { invoiceIds, confirmReplaced }, or invoiceIds is empty, has more than 1,000 ids or one longer than 64 characters.
  • 409 Conflict - Code REPLACED_UNCREDITED: an invoice named in invoiceIds is replaced and its credit notes don't cover it in full. Send confirmReplaced: true to export it anyway.
  • 422 Unprocessable Entity - Code NOTHING_TO_EXPORT: no approved, unpaid invoice is eligible.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/payment-export
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/payment-export" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "invoiceIds": [ "string" ],
   "confirmReplaced": boolean
}
Response · 200 OK
text/csv: one row per invoice, with a status column of approved_unpaid. related_invoice names what a debit note amends or what a replacement replaces; replaced_by, the last column, the invoices that replace one.

Get payment export

Downloads a payment export again.

GET/v1/workspaces/{regionId}/invoices/payment-exports/{exportId}

Parameters

  • exportIdstringRequired

    X-Pleaxy-Export-Id from ExportInvoicesForPayment.

Returns

200 OK with the object shown in the response.

More details

Downloads an export again: the same CSV, rebuilt from the invoices still exported under that export id. For when a download failed after the invoices were locked.

Errors

  • 404 Not Found - Code EXPORT_NOT_FOUND: no invoice is still exported under this id.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
GET /v1/workspaces/{regionId}/invoices/payment-exports/{exportId}
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/payment-exports/{exportId}" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
text/csv

Withdraw payment export

Takes an invoice back out of a payment export.

DELETE/v1/workspaces/{regionId}/invoices/{invoiceId}/payment-export

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Body

  • confirmNotPaidbooleanRequired

    Must be true: your payment run hasn't paid this invoice.

Returns

200 OK with the object shown in the response.

More details

Takes an invoice back out of its export, after you confirm your payment run hasn't paid it. Pleaxy can't see or cancel anything in your own payment tool. The invoice goes back to Approved but can't be exported or recorded again until the next business day, and the next export marks it previously_exported_at.

A signed-in session with 'edit' access to invoices; an API key gets 403 HUMAN_SESSION_REQUIRED.

Errors

  • 403 Forbidden - Code HUMAN_SESSION_REQUIRED, or a separation-of-duties refusal.
  • 404 Not Found - No invoice with this id exists in the workspace.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
DELETE /v1/workspaces/{regionId}/invoices/{invoiceId}/payment-export
Request
curl -X DELETE "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/payment-export" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "confirmNotPaid": true
}
Response · 200 OK
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Get remittance advice

Downloads the remittance advice for a payment, as a PDF.

GET/v1/workspaces/{regionId}/invoices/{invoiceId}/remittance-advice

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Returns

200 OK with the object shown in the response.

More details

The remittance advice for a recorded payment, as a PDF. It is marked as recorded by the buyer and not made through Pleaxy, and is not a receipt or proof of payment. Marked Revised after a re-record and Withdrawn after a withdrawal. Send Accept: application/pdf. A supplier whose buyers share an invoice id adds ?buyer=<buyer Workspace ID>.

Errors

  • 404 Not Found - No invoice with this id, or code NO_PAYMENT_RECORD: no payment was ever recorded for it.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
GET /v1/workspaces/{regionId}/invoices/{invoiceId}/remittance-advice
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/remittance-advice" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
application/pdf

Request remittance upload

Returns a URL to upload a proof of payment.

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/remittance-upload

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Body

  • contentType"application/pdf" | "image/png" | "image/jpeg"Required

    The file's type.

Returns

200 OK with the object shown in the response.

More details

A presigned URL to upload a proof of payment, such as the bank's transfer receipt, for an approved or paid invoice. PUT the file to uploadUrl with the same Content-Type within 5 minutes, up to maxBytes (10 MB), then send its fileId with SendRemittance or RecordPayment's remittance. An upload nothing attaches is deleted after a couple of days.

A signed-in session with 'edit' access to invoices; an API key gets 403 HUMAN_SESSION_REQUIRED. The same separation-of-duties rules as RecordPayment.

Errors

  • 400 Bad Request - contentType isn't a PDF, PNG or JPEG ("A remittance file must be a PDF, PNG or JPEG.").
  • 403 Forbidden - Code HUMAN_SESSION_REQUIRED, or a separation-of-duties refusal.
  • 404 Not Found - No invoice with this id exists in the workspace.
  • 409 Conflict - Code REMITTANCE_NEEDS_PAYMENT: the invoice isn't approved or paid yet.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/remittance-upload
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/remittance-upload" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "contentType": "application/pdf" | "image/png" | "image/jpeg"
}
Response · 200 OK
{
   "fileId": "string",
   "uploadUrl": "string",
   "maxBytes": number
}

Send remittance

Sends the supplier a proof of payment or a note.

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/remittance

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Body

  • notestring

    Up to 1,000 characters once cleaned, for the supplier. It goes in the supplier's notification, which its Slack or Teams channel also gets when routed there.

  • file{ fileId, fileName }

    A proof of payment, such as the bank's transfer receipt: the fileId from RequestRemittanceUpload, after the file was PUT to its uploadUrl, and a fileName of 1–200 characters.

Returns

201 Created with the object shown in the response.

More details

Sends the supplier a remittance for a paid invoice: a proof of payment, a note, or both. The supplier is notified (invoice_remittance_sent, with the note) and reads it in the invoice's remittances, without who sent it; GetRemittanceFile opens the file. Up to 20 per invoice, and each stays when a payment record is withdrawn. This isn't the remittance advice Pleaxy generates from the payment record (GetRemittanceAdvice), nor the supplier's remittanceInstructions.

A signed-in session with 'edit' access to invoices; an API key gets 403 HUMAN_SESSION_REQUIRED. The same separation-of-duties rules as RecordPayment.

Response fields

  • remittancesArray

    Every remittance sent for the invoice, oldest first: { id (RM-…), sentAt, sentByName, note?, file?: { fileName, contentType, sizeBytes } }.

Errors

  • 400 Bad Request - Code REMITTANCE_EMPTY (neither a note nor a file), REMITTANCE_NOTE_INVALID (over 1,000 characters), REMITTANCE_FILE_NAME_INVALID, REMITTANCE_UPLOAD_INCOMPLETE (the fileId isn't one uploaded for this invoice, or the upload didn't finish: "The proof of payment hasn't finished uploading. Attach it again.") or REMITTANCE_FILE_TOO_LARGE (over 10 MB).
  • 403 Forbidden - Code HUMAN_SESSION_REQUIRED, or a separation-of-duties refusal.
  • 404 Not Found - No invoice with this id exists in the workspace.
  • 409 Conflict - Code REMITTANCE_NEEDS_PAYMENT (no payment is recorded yet: record it first), REMITTANCE_LIMIT (it already has 20), or PAYMENT_CHANGED.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/remittance
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/remittance" \
  -H "X-Api-Key: $PLEAXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json
body.json
{
   "note": "string",
   "file": { "fileId": "string", "fileName": "string" }
}
Response · 201 Created
{
   "documentType": "invoice" | "debit_note",
   "amendsInvoiceId": "string",
   "amendsExternalInvoiceNumber": "string",
   "debitNoteIds": [ "string" ],
   "replacesInvoiceIds": [ "string" ],
   "replacedByInvoiceIds": [ "string" ],
   "replacement": { "creditedInFull": boolean, "uncredited": number },
   "uncreditedOriginals": [ { "invoiceId": "string", "externalInvoiceNumber": "string", "uncredited": number, "currency": "string" } ],
   "id": "string",
   "po": "string",
   "poLineNo": number,
   "billsNoPoLine": true,
   "buyerOrgId": "string",
   "supplierOrgId": "string",
   "supplier": "string",
   "amount": number,
   "currency": "string",
   "usageAmount": number,
   "usageCurrency": "string",
   "tolerancePct": number,
   "matchStatus": "pending" | "matched" | "discrepancy" | "unverified",
   "unverifiedReason": "no_purchase_order" | "no_billing_connection" | "debit_note",
   "discrepancyReason": "amount" | "currency",
   "paid": boolean,
   "dueInDays": number,
   "approval": {
      "requester": "string",
      "requesterEmployeeKey": "string",
      "groups": [
        {
          "id": "string",
          "mode": "sequential" | "parallel",
          "label": "string",
          "status": "pending" | "approved" | "rejected",
          "stages": [
            { "id": "string", "label": "string", "approverEmployeeKey": "string", "approverName": "string", "status": "pending" | "approved" | "rejected" }
          ]
        }
      ],
      "groupIndex": number,
      "status": "pending" | "approved" | "rejected",
      "history": [
        { "stage": "string", "by": "string", "at": "string", "action": "approved" | "rejected" | "limit_exceeded" | "reassigned" | "skipped" }
      ],
      "autonomy": boolean,
      "selfApproved": boolean
    },
   "createdAt": "string",
   "lastRemindedAt": "string",
   "rejection": { "reasonCode": "amount_incorrect" | "not_ordered_or_not_received" | "duplicate" | "missing_or_wrong_po" | "wrong_billing_entity" | "other", "reason": "string", "at": "string" },
   "replacesInvoiceId": "string",
   "source": "po-flip" | "direct" | "manual" | "feed",
   "submittedFormat": "json" | "cxml" | "ubl",
   "submittedBy": "string",
   "externalInvoiceNumber": "string",
   "lineItems": [
      { "description": "string", "qty": number, "unitPrice": number, "discount": number, "charge": number, "amount": number }
   ],
   "adjustments": [
      { "type": "discount" | "charge", "description": "string", "amount": number }
   ],
   "taxAmount": number,
   "taxBreakdown": [ { "description": "string", "rate": number, "amount": number } ],
   "paymentCurrencyAmount": { "currencyCode": "string", "totalAmount": number, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "taxCurrencyAmount": { "currencyCode": "string", "amountBreakdown": { "taxes": { "totalAmount": number } }, "currencyExchangeDetails": { "rate": number, "rateDate": "string", "rateSource": "string" } },
   "baseCurrencyAmount": { "currencyCode": "string", "totalAmount": number },
   "rateWarnings": [
      { "object": "payment" | "tax", "currency": "string", "statedRate": number, "referenceRate": number, "source": "provider" | "override", "asOf": "string", "deviationPct": number }
   ],
   "withholdings": [ { "description": "string", "rate": number, "amount": number } ],
   "deductions": [ { "kind": "prepayment" | "credit", "description": "string", "amount": number, "reference": "string", "paidOn": "string", "sourceInvoiceId": "string", "stated": true } ],
   "invoiceKind": "prepayment",
   "creditNoteIds": [ "string" ],
   "creditApplications": [ { "id": "string", "creditNoteId": "string", "externalCreditNoteNumber": "string", "amount": number, "appliedAt": "string", "appliedByName": "string", "auto": true } ],
   "payment": { "status": "recorded", "method": "ach" | "wire" | "check" | "card" | "other" | "credit", "paymentDate": "string", "reference": "string", "paidAmount": { "amount": number, "currency": "string", "rate": number, "rateDate": "string" } },
   "remittances": [ { "id": "string", "sentAt": "string", "sentByName": "string", "note": "string", "file": { "fileName": "string", "contentType": "application/pdf" | "image/png" | "image/jpeg", "sizeBytes": number } } ]
}

Get remittance file

Returns a short-lived link to a remittance's proof of payment.

GET/v1/workspaces/{regionId}/invoices/{invoiceId}/remittances/{remittanceId}/file

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

  • remittanceIdstringRequired

    The remittance, e.g. "RM-1A2B3C4D", from the invoice's remittances.

  • buyerstring

    Supplier callers only, and only needed after a 409 *_ID_AMBIGUOUS: the buyer's Workspace ID (ending in .buyer) or region id, from the 409's buyers list. Only that buyer's document is considered; none there is a 404. A .supplier Workspace ID is 400 BUYER_QUALIFIER_INVALID ("The buyer parameter must be one buyer's Workspace ID (ending in .buyer), from the buyers list in the 409 response."). Ignored for a Buyer caller.

Returns

200 OK with the object shown in the response.

More details

A link, valid for 5 minutes, to the proof of payment a remittance carries, opened inline. The supplier reads the remittances the buyer sent on its own invoice; the same lookup as GetInvoice.

Errors

  • 404 Not Found - No invoice with this id, or code REMITTANCE_NOT_FOUND: the invoice has no remittance with this id that carries a file.
  • 409 Conflict - Supplier callers only. Code INVOICE_ID_AMBIGUOUS: more than one of your buyers holds an invoice from you with this id (ids are unique only within a buyer), so Pleaxy won't guess which one you mean. Nothing is read or changed. The body's buyers lists each buyer's Workspace ID and name: repeat the call with ?buyer=<that Workspace ID>.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
GET /v1/workspaces/{regionId}/invoices/{invoiceId}/remittances/{remittanceId}/file
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/remittances/{remittanceId}/file" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
{
   "url": "string",
   "fileName": "string",
   "contentType": "application/pdf" | "image/png" | "image/jpeg"
}

Get stripe payment

Returns the state of a card or bank payment through Pleaxy (not available yet).

GET/v1/workspaces/{regionId}/invoices/{invoiceId}/stripe-payment

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Returns

200 OK with the object shown in the response.

More details

Not available yet: paying through Pleaxy isn't switched on, so this answers available: false with reason not_configured. Once it is, it says whether this invoice can be paid through Pleaxy with Stripe, or why not, and any Stripe payment under way. quote is what the buyer would pay: the invoice plus Stripe's bank-transfer fee (0.8%, capped at $5), which the buyer covers so the supplier receives the full invoice amount.

Errors

  • 404 Not Found - No invoice with this id exists in the workspace.
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
GET /v1/workspaces/{regionId}/invoices/{invoiceId}/stripe-payment
Request
curl "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/stripe-payment" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 200 OK
{
   "available": boolean,
   "reason": "not_configured" | "not_approved" | "payment_locked" | "supplier_not_on_pleaxy" | "supplier_not_connected" | "unsupported_currency" | "amount_too_small",
   "state": "checkout_open" | "processing" | "succeeded",
   "paymentMethodType": "us_bank_account",
   "quote": { "amount": number, "fee": number, "total": number, "currency": "USD" }
}

Start stripe payment

Starts paying an invoice through Pleaxy (not available yet).

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/stripe-payment

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

  • confirmReplacedboolean

    true to pay it anyway when the invoice is replaced (credit and rebill) and its credit notes don't cover it in full.

Returns

201 Created with the object shown in the response.

More details

Not available yet: paying through Pleaxy isn't switched on, so this answers 503. Once it is, it starts paying an approved invoice in full through Pleaxy, by US bank transfer with Stripe, and returns the url of a Stripe Checkout page. The payment is made on the supplier's own Stripe account: the money goes from the buyer's bank account to the supplier and never through Pleaxy. The buyer pays the invoice plus Stripe's fee, so the supplier receives the full invoice amount; Pleaxy charges nothing. The invoice is locked while Checkout is open (up to an hour) and marked paid when Stripe confirms the transfer. Resumes an open Checkout; answers { state } with no url when a payment is already processing or done.

A signed-in session with 'edit' access to invoices; an API key gets 403 HUMAN_SESSION_REQUIRED. The same separation-of-duties rules as RecordPayment.

Errors

  • 403 Forbidden - Code HUMAN_SESSION_REQUIRED, or a separation-of-duties refusal.
  • 409 Conflict - Code PAYMENT_ALREADY_RECORDED, PAYMENT_ACTIVE (exported or recorded: withdraw that first), REPLACED_UNCREDITED (the invoice is replaced and its credit notes don't cover it in full: add ?confirmReplaced=true to pay it anyway) or PAYMENT_CHANGED.
  • 422 Unprocessable Entity - Code STRIPE_SUPPLIER_NOT_ON_PLEAXY, STRIPE_SUPPLIER_NOT_CONNECTED, STRIPE_UNSUPPORTED_CURRENCY or STRIPE_AMOUNT_TOO_SMALL.
  • 503 Service Unavailable - Paying through Pleaxy isn't switched on ("Paying suppliers through Pleaxy isn't available yet.").
  • 401 Unauthorized - No credential was presented, or the Bearer token / X-Api-Key value is missing, invalid, expired, or revoked.
  • 403 Forbidden - The caller's Bearer token or API key doesn't grant access to this workspace (regionId), or lacks the required role.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/stripe-payment
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/stripe-payment" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 201 Created
{
   "url": "string",
   "state": "checkout_open" | "processing" | "succeeded"
}

Settle invoice

Retired: use RecordPayment instead.

POST/v1/workspaces/{regionId}/invoices/{invoiceId}/settle

Parameters

  • invoiceIdstringRequired

    The invoice identifier.

Returns

410 Gone with the object shown in the response.

More details

Retired. Always answers 410 SETTLE_REPLACED and changes nothing. Record a payment made outside Pleaxy with RecordPayment.

Changed 2026-09-24, a security fix inside v1: settling was narrowed to signed-in sessions and then retired; it never pays anything.

Errors

  • 410 Gone - Code SETTLE_REPLACED, always.
POST /v1/workspaces/{regionId}/invoices/{invoiceId}/settle
Request
curl -X POST "$PLEAXY_API_BASE_URL/v1/workspaces/{regionId}/invoices/{invoiceId}/settle" \
  -H "X-Api-Key: $PLEAXY_API_KEY"
Response · 410 Gone
{
   "statusCode": 410,
   "error": "Gone",
   "code": "SETTLE_REPLACED",
   "message": "string",
   "messageId": "invoices.settleReplaced",
   "params": { }
}
Talk to us