Save Payees as Contacts

Save a third-party payee once (with their bank account, mailing address, international account, PPI, or email for virtual cards) and reuse it across payout and collection rails.

A Contact is a saved, reusable third party you pay or collect from: a vendor, contractor, employee, or another business. You store their payout details once (a bank account, mailing address, international account, Passport Payment ID, or email for virtual cards), then reference the saved contact by id on every future transaction instead of re-entering details each time.

A single contact can bundle more than one payment instrument, so you can reach the same payee across different rails. To save the customer's own bank account, use External Accounts instead; Contacts are for third parties.

For the complete field list, jump to the Request reference.

🏦

Contacts are for third parties; External Accounts are for your own money

A Contact stores a third party's payout details (their bank account, international account, mailing address, PPI, or email for virtual cards) inside the contact entity, so you can Pay or Collect from them. To save an account you own, use External Accounts or International External Accounts instead; those feed Transfer funds. Don't save your own account as a Contact, and don't use an External Account to pay or collect from a third party.


How a contact works

  • Create the contact with one or more payment instruments: externalAccount, internationalExternalAccount, mailingAddress, a ppi, or an email (for virtual-card delivery).
  • Verification runs per instrument. Bank-account instruments are OFAC-screened on the holder name (and validated like External Accounts); a ppi instrument is verified against an existing Passport account and is usable immediately.
  • Reference the contact id when you create a payout or collection. One saved contact serves many transactions.

Save a payee once and reuse it. Fewer re-keys means faster initiation, fewer errors, and cleaner reconciliation.


Base path

Contacts are created under a customer:

Base pathDescription
/v1/customer/id/{customerId}/contactCustomer-owned contacts (by Passport ID)
/v1/customer/externalId/{customerExternalId}/contactCustomer-owned contacts (by program-manager externalId)

Every create, update, verify, and configuration request requires the PromiseMode header (NEVER for an immediate response, ALWAYS to defer processing).


Create your first contact

The fastest way to see it work: save a vendor's US bank account so you can pay them by ACH or wire later.

Request

POST /v1/customer/id/{customerId}/contact
PromiseMode: NEVER

{
  "externalId": "CON-EXT-0001",
  "name": "Acme Supplier",
  "contactType": "BUSINESS",
  "legalName": "Acme Supplier LLC",
  "externalAccount": [
    {
      "holderName": "Acme Supplier LLC",
      "holderType": "CORPORATE",
      "accountNumber": "123456789012",
      "routingNumber": "021000021",
      "wireRoutingNumber": "021000021",
      "type": "CHECKING"
    }
  ]
}

Response: 201 Created

Create returns 201 Created with an empty body; the new contact's ID is in the url (Location) response header. (With PromiseMode: ALWAYS, you get 202 Accepted and processing continues asynchronously.)

HTTP/1.1 201 Created
url: /v1/customer/id/{customerId}/contact/id/5501

Retrieve the contact to see its instruments and their verification status:

GET /v1/customer/id/{customerId}/contact/id/5501

{
  "resourceName": "contact",
  "url": "/v1/customer/id/{customerId}/contact/id/5501",
  "id": 5501,
  "externalId": "CON-EXT-0001",
  "name": "Acme Supplier",
  "contactType": "BUSINESS",
  "legalName": "Acme Supplier LLC",
  "externalAccount": [
    {
      "resourceName": "externalAccount",
      "id": 4000364,
      "holderName": "Acme Supplier LLC",
      "holderType": "CORPORATE",
      "accountNumberLast4": "9012",
      "routingNumber": "021000021",
      "status": "INACTIVE",
      "verification": { "ofacStatus": "PENDING_VERIFICATION" }
    }
  ]
}

Use externalId for safe retries

For Treasury, PCE treats the externalId in the request body as the idempotency key; there's no separate header. Send a unique externalId per contact; retrying with the same value creates it only once.


Scenarios

Save a payee by providing one or more instruments, then reference the contact on future transactions. Bank instruments are OFAC-screened; PPI instruments are usable immediately.

sequenceDiagram
    participant App as Your application
    participant PCE as PCE
    participant OFAC as OFAC Screening

    App->>PCE: POST /contact (name + instrument)
    PCE->>PCE: Field validation
    PCE-->>App: 201 Created (id in url header)
    alt Bank-account instrument
        PCE->>OFAC: Screen holder name
        OFAC-->>PCE: Cleared / Rejected
        PCE-->>App: Webhook (instrument ACTIVE or BLOCKED)
    else PPI instrument
        PCE->>PCE: Verify PPI exists
        PCE-->>App: Instrument usable immediately
    end
    Note over App: Reference contact.id in future transactions

Before you begin (all scenarios)

  • Customer exists and is ACTIVE.
  • You have the payee's instrument details (bank account, mailing address, PPI, international account, or email for a virtual card).
  • Your webhook endpoint is subscribed to contact/external-account events (to track verification).

Scenario 1: Save a third-party US bank account (ACH/Wire)

Save a third-party US bank account so you can later pay them by ACH or domestic wire.

You'll also need: the payee's routing number, account number, account type, and holder type (CONSUMER / CORPORATE).

POST /v1/customer/id/{customerId}/contact
PromiseMode: NEVER

{
  "externalId": "contact-vendor-001",
  "name": "Acme Supplies",
  "contactType": "BUSINESS",
  "legalName": "Acme Supplies LLC",
  "externalAccount": [
    {
      "holderName": "Acme Supplies LLC",
      "holderType": "CORPORATE",
      "accountNumber": "987654321098",
      "routingNumber": "021000089",
      "wireRoutingNumber": "021000089",
      "type": "CHECKING"
    }
  ]
}

Scenario 2: Save a mailing address (check)

Save a third-party mailing address so you can later pay them by mailed check.

You'll also need: the recipient's name and a US mailing address.

POST /v1/customer/id/{customerId}/contact
PromiseMode: NEVER

{
  "externalId": "contact-check-002",
  "name": "Jane Doe",
  "contactType": "INDIVIDUAL",
  "firstName": "Jane",
  "lastName": "Doe",
  "mailingAddress": [
    {
      "name": "Jane Doe",
      "addressLine1": "100 Market St",
      "city": "San Jose",
      "state": "CA",
      "zip": "95113"
    }
  ]
}

Scenario 3: Save a payee by Passport Payment ID (PPI)

Save a payee who has a Passport Account by their Passport Payment ID, so you can later pay them by BOOK transfer. PPI instruments skip external validation and are usable immediately.

You'll also need: the payee's PPI. Confirm it first with the Verify PPI API.

POST /v1/customer/id/{customerId}/contact
PromiseMode: NEVER

{
  "externalId": "contact-ppi-003",
  "name": "Bright Tech",
  "contactType": "BUSINESS",
  "legalName": "Bright Tech Inc",
  "ppi": "brighttech@ppi"
}

Scenario 4: Save a third-party international bank account

Save a third party's overseas bank account so you can later pay them by international wire. Field rules follow International External Accounts (valid SWIFT code, accepted currency, holder address).

You'll also need: the SWIFT code, account number/IBAN, accepted currency, and holder address.

POST /v1/customer/id/{customerId}/contact
PromiseMode: NEVER

{
  "externalId": "contact-intl-004",
  "name": "Globex GmbH",
  "contactType": "BUSINESS",
  "legalName": "Globex GmbH",
  "internationalExternalAccount": [
    {
      "type": "CHECKING",
      "holderName": "Globex GmbH",
      "holderType": "CORPORATE",
      "accountNumber": "DE89370400440532013000",
      "swiftCode": "DEUTDEFF",
      "acceptedCurrency": ["EUR"],
      "purpose": "Services",
      "holderAddress": {
        "addressLine1": "Friedrichstrasse 100",
        "city": "Berlin",
        "country": "DE",
        "zip": "10117"
      }
    }
  ]
}

Scenario 5: Save a payee's email for virtual cards

Save a payee's email so you can later send them a Virtual Card. When you send a VIRTUAL_CARD transaction to this saved contact, the Virtual Card is delivered to this email address. To load and send the card, see Fund a virtual card.

You'll also need: the recipient's name and a valid email.

POST /v1/customer/id/{customerId}/contact
PromiseMode: NEVER

{
  "externalId": "contact-vcard-005",
  "name": "Sam Lee",
  "contactType": "INDIVIDUAL",
  "firstName": "Sam",
  "lastName": "Lee",
  "email": "[email protected]"
}

Verify a PPI

Before saving or paying a PPI-based payee, confirm the Passport Payment ID exists. Use the POST /v1/ppi/verify API.

POST /v1/ppi/verify
PromiseMode: NEVER

{
  "ppi": "brighttech@ppi"
}

A 204 No Content response means the PPI exists and is valid (no body is returned). A 299 means it was verified but with a warning to review. An invalid or missing PPI returns 400.


Retrieve, list, and manage contacts


Payment acceptance configuration (optional)

You can attach payment-acceptance rules to a contact, for example, to constrain a method to an exact amount. Use the Add Contact Configurations API; read them back with Get Contact Configuration.

POST /v1/customer/id/{customerId}/contact/id/{contactId}/configuration
PromiseMode: NEVER

{
  "paymentAcceptanceCriteria": [
    {
      "method": ["MONEYGRAM"],
      "matchType": "EXACT",
      "amount": "1000.00"
    }
  ]
}

A successful call returns 204 No Content.


Request reference

Contact core fields

ParameterRequiredTypeDescription
nameStringDisplay label for the contact
contactTypeConditionalStringINDIVIDUAL or BUSINESS
firstNameConditionalStringFor INDIVIDUAL contacts
lastNameConditionalStringFor INDIVIDUAL contacts
legalNameConditionalStringFor BUSINESS contacts
emailConditionalStringRequired when you'll send the contact a Virtual Card (method VIRTUAL_CARD); the Virtual Card is delivered to this email address. Format [email protected]
ppiConditionalStringPassport Payment ID of an internal payee
externalIdRecommendedStringYour reference ID; used as the idempotency key

Provide at least one payment instrument below (a contact can hold several).

Instrument: externalAccount[] (domestic bank account)

ParameterRequiredDescription
holderNameAccount holder name as on bank records
holderTypeCONSUMER or CORPORATE
accountNumberBank account number
routingNumber9-digit ACH routing number
typeSAVINGS or CHECKING
wireRoutingNumberOptional9-digit routing number for wires, if different
holderPhoneOptionalHolder phone number
holderAddressOptionalHolder address (addressLine1, city, state, zip)

Instrument: internationalExternalAccount[]

Field rules match International External Accounts: type, holderName, holderType, accountNumber, swiftCode, acceptedCurrency (array), holderAddress, and conditional internationalRoutingCode / purpose.

Instrument: mailingAddress[] (check)

ParameterRequiredDescription
addressLine1Street address (4-40 chars)
cityCity
stateTwo-character US state code
zip5- or 9-digit ZIP
nameOptionalName of the address owner
phoneOptionalPhone number

Virtual card delivery: email

To send a Virtual Card to a contact, save the contact with a valid email; no card instrument is stored on the contact. When you later send a VIRTUAL_CARD transaction referencing this contact, the Virtual Card is delivered to that email address.

ParameterRequiredDescription
email✓ (for virtual card)Recipient email where the Virtual Card is delivered. Format [email protected]

Verification & status

  • Bank-account instruments are OFAC-screened on the holder name and follow the same lifecycle as External Accounts: they start INACTIVE, become ACTIVE once screening and validation clear, and are BLOCKED if OFAC/validation fails. Each instrument in the retrieve response carries its own status and verification.ofacStatus.
  • PPI instruments are verified against an existing Passport account and are usable immediately.
  • Track verification with contact / external-account webhooks, or by retrieving the contact and checking each instrument's status.

Validation

When you submit the request, we validate:

CheckError if failed
name present400: Required field
At least one valid instrument provided400: Missing instrument
Bank instrument has accountNumber, routingNumber, holderName, holderType, type400: Missing field
SWIFT code valid (international)400: Invalid routing code
PPI exists (PPI instrument)400: Invalid PPI
Contact exists and not deleted (on retrieve/update)404: Not found
Contact belongs to the path customer403: Owner mismatch

Best practices

PracticeDescription
Set a unique externalIdUsed as the idempotency key, retries never create duplicates
Verify a PPI before savingConfirms the internal payee exists
Reuse contacts for repeat paymentsFaster initiation, fewer errors, cleaner reconciliation
Store the contact idRequired to pay, retrieve, update, or delete
Monitor instrument statusA payout can only use an ACTIVE bank instrument

Next steps

See also


Did this page help you?