Partner API Reference

Complete endpoint reference for partner integrations

Quick-reference for all partner-accessible endpoints. For flow diagrams, webhook verification examples, and PCI scope notes see PARTNER_GUIDE.html.


Base URL and Authentication

Environment Base URL
Production https://api.pay.emun1.com
Nonprod (testing) https://nonprod.pay.emun1.com
Authorization: ApiKey <your-api-key>
Content-Type: application/json

Every request is automatically scoped to your merchant account. API keys are generated in the portal under Settings → API Keys.


Payment Requests

Create a Payment Request

POST /v1/payment-requests

Request body

Field Type Required Description
referenceId string Yes Your order/invoice number. Returned in all webhooks.
buyerEmail string Yes Buyer email. Enables saved-card recall for returning buyers.
amount number Yes Amount in dollars (e.g. 312.50). Range: 0.01 – 1,000,000.
currency string No ISO 4217. Default: "USD". "CAD" is also supported.
captureMode string No "manual" (default) or "immediate".
acceptedPaymentMethod string No "card", "ach", or omit to let buyer choose.
buyerCompany string No Company name shown in the portal.
returnUrl string No Redirect after payment on mobile.
billingAddress object No See Billing Address.
lineItems array No See Line Items. Amounts in cents.

Response 201

{
  "id":        "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "token":     "abc123xyz...",
  "iframeUrl": "https://checkout.pay.emun1.com/enter/abc123xyz...",
  "status":    "Pending",
  "expiresAt": "2024-06-01T13:00:00Z"
}

Expires after 60 minutes.


Get Payment Request Status

GET /v1/payment-requests/{token}

Pass the string token (not the UUID) returned from the create call.

Response 200

{
  "id":                    "3fa85f64-...",
  "referenceId":           "ORD-20240601-001",
  "buyerEmail":            "buyer@example.com",
  "buyerCompany":          "Acme Wholesale",
  "amount":                312.50,
  "currency":              "USD",
  "status":                "Collected",
  "createdAt":             "2024-06-01T12:00:00Z",
  "expiresAt":             "2024-06-01T13:00:00Z",
  "completedAt":           "2024-06-01T12:34:56Z",
  "acceptedPaymentMethod": "card"
}
Status Meaning
Pending Waiting for buyer to complete payment.
Collected Card entered. Ready to authorize.
Expired 60-minute window elapsed.
Cancelled Cancelled by the platform.

Request Card Refresh

When a stored card has expired or been declined during authorization, send the buyer a link to enter a replacement card.

POST /v1/payment-requests/{id}/card-update

The {id} is the UUID of the source payment request (status must be Collected).

Request body

Field Type Required Description
returnUrl string No Mobile redirect after card entry.

Response 201

{
  "id":        "9b1b3a2e-...",
  "token":     "xyz789...",
  "iframeUrl": "https://checkout.pay.emun1.com/enter/xyz789...",
  "expiresAt": "2024-06-01T14:00:00Z"
}

Show or send the iframeUrl to the buyer. On completion:

Expires after 60 minutes.


HostedPCI Card Collection

A small number of merchants haven't migrated their actual payment processing to Adyen yet and still use a third-party vendor, HostedPCI, for PCI-compliant card capture. For these merchants, Foundry shows the HostedPCI iframe instead of the Adyen Drop-in inside the same iframeUrl you already embed — the POST /v1/payment-requests and GET /v1/payment-requests/{token} contracts above are unchanged, and the payment request still reaches Collected the same way.

The difference only shows up after collection: HostedPCI-mode payment requests never reach Authorized/Captured/etc. through FoundryPOST /v1/payments and the batch authorize/capture endpoints do not apply to them. In most cases you already process the charge yourself on your own downstream gateway (Authorize.Net, Stripe, etc.), using the vendor's own gateway credentials. To do that, retrieve a Gateway Token for the collected card:

Retrieve Gateway Token

POST /v1/payment-requests/{id}/gateway-token

The {id} is the UUID of a Collected payment request for a HostedPCI-mode merchant.

Response 200

{
  "gatewayToken":         "4111...tok",
  "gatewayResponseStatus": "Approved",
  "gatewayResponseCode":   "00"
}
Field Description
gatewayToken The vendor-specific token to send to your own downstream gateway. Can be empty when the vendor has no separate gateway configured (HostedPCI itself is the terminal processor) — this is expected, not an error.
gatewayResponseStatus / gatewayResponseCode The downstream gateway's own status/code for this tokenization, if any.

Call this whenever you need the token — right after the payment_request.completed webhook fires, or later. It's independent of card collection itself. A 409 means the payment request isn't a Collected HostedPCI-mode request; a 502 means the vendor's gateway tokenization itself failed (detail carries the underlying error).


Card Enrollment

Store a card on file without tying it to any specific order or invoice.

POST /v1/card-enrollments

Request body

Field Type Required Description
buyerEmail string Yes Buyer email. Used as the shopper identifier for returning buyers.
buyerReference string No Your own buyer/account ID. Returned in the card.enrolled webhook.
buyerCompany string No Company name (display only).
returnUrl string No Mobile redirect after card entry.
billingAddress object No Pre-populated in the form. See Billing Address.

Response 201

{
  "id":        "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "token":     "abc123xyz...",
  "iframeUrl": "https://checkout.pay.emun1.com/enter/abc123xyz...",
  "expiresAt": "2024-06-01T13:00:00Z"
}

Show the iframeUrl to the buyer. On completion you receive a card.enrolled webhook with the paymentMethodId. Use that ID in future POST /v1/payments calls.

Expires after 60 minutes.


Payments

Authorize

Place an authorization hold on the funds for a Collected payment request. This is the API equivalent of clicking Authorize on the payment request's detail page in the portal — useful if you'd rather trigger it from your own systems than have someone do it manually. Only applies to "manual" capture-mode payment requests — "immediate" ones are already authorized (and captured) automatically as soon as the buyer submits the payment form, and calling this endpoint on one returns an error.

POST /v1/payments

Request body

Field Type Required Description
paymentRequestId UUID Yes ID of the Collected payment request.

Response 200

{
  "id":            "7c4a9e1d-...",
  "status":        "Authorized",
  "pspReference":  "853612...",
  "amount":        312.50,
  "currency":      "USD",
  "referenceId":   "ORD-20240601-001",
  "authorizedAt":  "2024-06-01T12:35:00Z"
}
Payment Status Meaning
Authorized Funds held. Capture via portal to settle.
PartiallyCaptured One or more partial captures settled; remaining authorized balance is still open.
Captured Full authorized amount settled (immediate-capture mode or all captures complete).
Failed Authorization declined.
Voided Authorization cancelled before (or after partial) capture.
Refunded Post-capture refund issued.

Capture

Capture (settle) an authorized payment — the API equivalent of clicking Capture on the payment's detail page in the portal. Only Authorized or PartiallyCaptured payments can be captured.

POST /v1/payments/{id}/capture

Request body (optional — omit entirely to capture the full remaining balance)

Field Type Required Description
amount number No Amount to capture. Defaults to the full remaining authorized balance. Use for split-shipment workflows — capture again later with the remaining amount once the rest ships.

Response 200

{
  "id":              "7c4a9e1d-...",
  "status":          "Captured",
  "authorizedAmount": 312.50,
  "capturedAmount":   312.50,
  "remainingAmount":  0,
  "currency":        "USD",
  "referenceId":     "ORD-20240601-001",
  "capturedAt":      "2024-06-01T13:00:00Z"
}

A capture smaller than the remaining authorized amount moves status to PartiallyCaptured rather than Captured — submit another Capture call against the same id later for the rest. If you're capturing many payments at once, Batch Capture below does the same thing across a whole list in one call, including partial/split-shipment amounts.


Void

Cancel the outstanding, uncaptured portion of an authorization — the API equivalent of clicking Void on the payment's detail page in the portal. Only Authorized or PartiallyCaptured payments can be voided; any amount already captured before voiding stays captured and settled — voiding only cancels what's left of the hold.

POST /v1/payments/{id}/void

No request body.

Response 200

{
  "id":              "7c4a9e1d-...",
  "status":          "Voided",
  "authorizedAmount": 312.50,
  "capturedAmount":   0,
  "currency":        "USD",
  "referenceId":     "ORD-20240601-001"
}

Refund

Refund a captured payment, in full or in part. Refunds settle asynchronously — the response reflects the refund request being accepted, not final confirmation from the card network.

POST /v1/payments/{id}/refund

Request body

Field Type Required Description
reason string Yes One of RequestedByCustomer, IssueWithItemSold, Fraudulent, Duplicate, Other.
amount number No Amount to refund. Defaults to the full refundable balance (captured amount minus any prior refunds).

Response 200

{
  "id":              "7c4a9e1d-...",
  "status":          "Refunded",
  "authorizedAmount": 312.50,
  "capturedAmount":   312.50,
  "referenceId":     "ORD-20240601-001"
}

status moves to Refunded only once the refund covers the full captured amount — a partial refund leaves status as Captured, with the partial refund tracked internally against that payment. Only Captured (or already-partially-refunded) payments can be refunded.


Batch Authorize

Authorize multiple payment requests in one call. Always returns HTTP 200; inspect each item's success field — the batch status code does not indicate per-item success.

POST /v1/payments/batch-authorize

Request body

{
  "items": [
    { "referenceId": "ORD-001" },
    { "referenceId": "ORD-002", "amount": 150.00 }
  ]
}
Field Type Required Description
items[].referenceId string Yes Your order/invoice reference.
items[].amount number No Override the payment request amount (advanced use).

Response 200

{
  "results": [
    { "referenceId": "ORD-001", "success": true,  "paymentId": "7c4a9e1d-..." },
    { "referenceId": "ORD-002", "success": false, "error": "No collected payment request found" }
  ],
  "summary": { "total": 2, "succeeded": 1, "failed": 1 }
}

Batch Capture

Capture multiple authorized (or partially captured) payments in one call. Supports partial amounts and per-capture shipment references for split-shipment workflows.

POST /v1/payments/batch-capture

Request body

{
  "items": [
    { "referenceId": "ORD-001" },
    { "referenceId": "ORD-002", "amount": 150.00, "shipmentReference": "SHIP-A" }
  ]
}
Field Type Required Description
items[].referenceId string Yes Your order/invoice reference.
items[].amount number No Amount to capture; defaults to the full remaining balance. Use for split-shipment workflows.
items[].shipmentReference string No Optional tag for this capture (e.g. "SHIP-A"). Returned in capture history for reconciliation.

Response 200 — same per-item { referenceId, success, paymentId, error } format as Batch Authorize. Always HTTP 200; inspect each item's success field.


Authorization Extension & Reauthorization

Manual-capture authorizations left uncaptured for too long can expire on the card network before you capture them. Foundry proactively tries to keep an aging authorization alive, and falls back to transparently re-authorizing the remaining balance (same stored payment method, new authorization) if that isn't possible.

This means pspReference on a payment is not permanently fixed — it reflects the current live authorization and can change if a reauthorization happens. Use id (the Foundry payment ID) or your own referenceId as your stable identifier for reconciliation, not pspReference.

Payment Status Meaning
Expired The authorization expired before capture and could not be automatically reauthorized. Nothing was captured. A partially-captured payment whose remaining balance suffers this stays PartiallyCaptured — only the un-captured remainder is affected.

GET /v1/transactions includes an authorizationEvents array per payment recording any extension or reauthorization attempts (outcome, and — for a successful reauthorization — the old and new pspReference), useful for reconciliation if you track PSP references on your side.


Transactions

List Transactions

GET /v1/transactions

Query parameters

Parameter Type Description
status string Filter by payment status (e.g. Authorized, Captured, Expired).
search string Full-text search on reference ID or buyer email.
paymentRequestId UUID Return only transactions for a specific payment request.
from ISO 8601 Earliest transaction date.
to ISO 8601 Latest transaction date.
page integer Page number (1-based).
pageSize integer Results per page (default 20).

Response 200 — paginated list of payment objects. Each includes an authorizationEvents array — see "Authorization Extension & Reauthorization" above.


Payouts

Deposit reconciliation — what was paid into your bank account, and which sales, refunds, and chargebacks made up each deposit. A different question from Transactions above (which is per-transaction economics) — see the portal's own Payouts page for the equivalent UI.

List Payouts

GET /v1/payouts

Query parameters

Parameter Type Required Description
from ISO 8601 date Yes Earliest deposit date.
to ISO 8601 date Yes Latest deposit date.
search string No Matches order/invoice reference or PSP reference of a transaction within the deposit.
minAmount number No Minimum deposit (net) amount.
maxAmount number No Maximum deposit (net) amount.
page integer No Page number (1-based).
pageSize integer No Results per page (default 25).

Response 200

{
  "total": 5,
  "page": 1,
  "pageSize": 25,
  "items": [
    {
      "payoutDate":       "2024-06-16T07:00:25Z",
      "grossAmount":      13318.96,
      "feeAmount":        145.00,
      "netAmount":        13173.96,
      "currency":         "USD",
      "transactionCount": 6
    }
  ]
}

payoutDate is the deposit's unique identifier for the two endpoints below — pass it back exactly as returned.


Get Payout Detail

Every transaction, refund, and chargeback that composed one specific deposit.

GET /v1/payouts/detail

Query parameters

Parameter Type Required Description
payoutDate ISO 8601 Yes The exact payoutDate value from a List Payouts item.

Response 200

{
  "items": [
    {
      "id":            "8f2a1c3e-...",
      "lineType":      "Capture",
      "description":   "Seller split",
      "amount":        9412.80,
      "currency":      "USD",
      "pspReference":  "TSFTZXXT98KPT9V5",
      "referenceId":   "ORD-1005",
      "customerName":  "Acme Wholesale"
    }
  ]
}
lineType Meaning
Capture A sale's net proceeds credited toward this deposit.
Refund A refund debited from this deposit.
Chargeback A chargeback debited from this deposit.

referenceId/customerName are null when the line couldn't be matched back to one of your own payments (rare — see DEVGUIDE if you need the details).


Get Payout Summary

Aggregate totals for a date range — the same figures shown on the portal's Payouts page.

GET /v1/payouts/summary

Query parameters

Parameter Type Required Description
from ISO 8601 date Yes Start of range.
to ISO 8601 date Yes End of range.

Response 200

{
  "depositsAmount":    13173.96,
  "depositsCount":     1,
  "transactionCount":  6,
  "chargebacksAmount": 0,
  "refundsAmount":     145.00,
  "totalDeductions":   145.00,
  "netPayout":         13173.96
}

A chargeback or refund can be booked in a different deposit period than its original sale — totalDeductions and the change in depositsAmount may not tie out exactly for a given range for that reason; this is normal payout-reconciliation timing, not a data error.


Export Payouts (CSV)

GET /v1/payouts/export

Query parameters

Parameter Type Required Description
from ISO 8601 date Yes Start of range.
to ISO 8601 date Yes End of range.
mode string No "payouts" (default) — one row per deposit. "details" — one row per transaction/refund/chargeback, with its parent deposit's date/amount repeated on each row.

Response 200text/csv, filename payouts-{mode}.csv.


Shared Object Schemas

Billing Address

{
  "street":          "123 Main St",
  "houseNumberOrName": "Suite 400",
  "city":            "Chicago",
  "stateOrProvince": "IL",
  "postalCode":      "60601",
  "country":         "US"
}

All fields optional; any provided are forwarded to the card network for AVS.

Line Items

Array of objects for Level 3 interchange qualification. Amounts in cents.

[
  {
    "id":                   "SKU-001",
    "description":          "Widget A",
    "quantity":             2,
    "amountExcludingTax":   25000,
    "amountIncludingTax":   25000,
    "taxAmount":            0,
    "taxPercentage":        0,
    "commodity":            "widgets",
    "upc":                  "012345678905",
    "brand":                "Acme",
    "imageUrl":             "https://yourapp.example.com/images/widget-a.png",
    "productUrl":           "https://yourapp.example.com/products/widget-a"
  }
]

Any other fields you include (e.g. your own internal productCode) are stored but stripped before being sent to Adyen — only the fields above are forwarded for L2/L3 enrichment.


Webhooks

Envelope

All webhooks share a common envelope:

{
  "eventType": "payment_request.completed",
  "createdAt": "2024-06-01T12:34:56Z",
  "data":      { }
}

Event Payloads

payment_request.completed

{
  "referenceId":     "ORD-20240601-001",
  "status":          "collected",
  "paymentMethodId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

card.enrolled

{
  "buyerReference":  "BUYER-4521",
  "buyerEmail":      "buyer@example.com",
  "paymentMethodId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "last4":           "1111",
  "brand":           "visa"
}

card.updated

{
  "referenceId":     "ORD-20240601-001",
  "paymentMethodId": "7c4a9e1d-...",
  "last4":           "4242",
  "brand":           "mastercard"
}

payment_request.expired

{
  "referenceId": "ORD-20240601-001"
}

Signature Verification

Every webhook includes X-Foundry-Signature: sha256=<hex-digest>.

import hmac, hashlib

def verify(secret: str, body: bytes, header: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header)
bool Verify(string secret, byte[] body, string header)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var digest = "sha256=" + Convert.ToHexString(hmac.ComputeHash(body)).ToLower();
    return CryptographicOperations.FixedTimeEquals(
        Encoding.UTF8.GetBytes(digest),
        Encoding.UTF8.GetBytes(header));
}

Respond with any 2xx to acknowledge. Failed deliveries are retried 3 times with exponential backoff.


Error Responses

{ "error": "Payment request not found or not in collected state" }
HTTP Status Meaning
400 Missing required field or invalid value.
401 Invalid or missing API key.
404 Resource not found.
409 Conflict — e.g. payment request already collected or expired.
422 Payment could not be processed (e.g. declined).
502 Upstream error from payment network. Retry after a short delay.

Test Cards

Scenario Card Number Expiry CVV
Visa — success 4111 1111 1111 1111 Any future Any 3 digits
Mastercard — success 5500 0000 0000 0004 Any future Any 3 digits
Declined 4000 0000 0000 0002 Any future Any 3 digits
Insufficient funds 4000 0000 0000 9995 Any future Any 3 digits

ACH testing: routing 021000021, any 10-digit account number.