Save Payees as Contacts
Save a payee once and reuse it for fast, repeat payouts.
A Contact stores a third party's payout details — a bank account, mailing address, international account, virtual-card email, or a Passport account (PPI) — so you can pay them by referencing the saved contact instead of re-entering details each time. Contacts are for paying other people and businesses. To save your own external bank account, use External Accounts instead.
A single contact can hold more than one destination, so you can pay the same payee across different rails. For the complete field list, jump to the Request reference.
Common use cases
- Save a vendor or supplier you pay by ACH or wire
- Save a recipient you pay by mailed check
- Save a payee who has a Passport account
- Save a recipient abroad for international wires
- Save a recipient's email for a virtual card
Create your first contact
The fastest way to see it work: save a vendor's bank account with the minimal fields below. The full set of options is in the Request reference.
POST /v1/customer/id/{id}/contact
{
"externalId": "contact-vendor-001",
"name": "Acme Supplies",
"contactType": "BUSINESS",
"legalName": "Acme Supplies LLC",
"externalAccount": {
"accountNumber": "987654321098",
"routingNumber": "021000089",
"type": "CHECKING",
"holderType": "CORPORATE"
}
}You'll get back a contact with a unique id. Store it — you'll reference it when you pay the contact and when you fetch, update, or delete it.
UseexternalIdfor safe retriesFor Treasury, PCE treats the
externalIdin the request body as the idempotency key — there's no separate header. Send a uniqueexternalIdper contact; retrying with the same value creates it only once.
Scenarios
A contact is created the same way every time — you POST the contact with a destination that matches how you'll pay them, we validate it, and it becomes usable once verification clears. The only thing that changes per scenario is the destination details you save. Pick the one that matches how you'll pay this payee.
sequenceDiagram
participant You as Your application
participant PCE as Passport
You->>PCE: POST /contact (unique externalId)
PCE->>PCE: Validate (OFAC / account validation / PPI)
PCE-->>You: status ACTIVE or PENDING_VERIFICATION
PCE->>PCE: Verification completes
PCE-->>You: ACTIVE (usable) or FAILED
The status values above are described in full under Contact Statuses.
Before you begin (all scenarios)
- The customer the contact belongs to is active.
Each scenario below adds the destination details specific to how you'll pay the contact.
Eng to confirm — examplesRequest field values below are illustrative placeholders (the fields themselves come from the documented tables in the Request reference). The response bodies are representative and may be incomplete — modeled on the Treasury resource pattern, not captured from a live call. Verify the exact response fields (including
statusand the OFAC / account-validation verification details), and any cross-links to downstream pay flows, against live data before publishing.
Scenario 1: Pay a vendor by bank transfer
(save a third-party bank account for ACH or wire)
Use this for a vendor, supplier, or contractor you'll pay by ACH or domestic wire. Save their bank account once, then reference the contact on every payout — see Pay via ACH.
You'll also need: the recipient's account number, ACH routing number, account type, and holder type.
Request
POST /v1/customer/id/{id}/contact
{
"externalId": "contact-vendor-001",
"name": "Acme Supplies",
"contactType": "BUSINESS",
"legalName": "Acme Supplies LLC",
"externalAccount": {
"accountNumber": "987654321098",
"routingNumber": "021000089",
"type": "CHECKING",
"holderType": "CORPORATE"
}
}Response
{
"resourceName": "contact",
"url": "/v1/customer/id/4225975/contact/id/4019600",
"id": 4019600,
"externalId": "contact-vendor-001",
"name": "Acme Supplies",
"contactType": "BUSINESS",
"status": "PENDING_VERIFICATION",
"externalAccount": {
"resourceName": "externalAccount",
"id": 4019508,
"url": "/v1/customer/id/4225975/contact/id/4019600/externalAccount/id/4019508",
"status": "PENDING_VERIFICATION"
}
}Once the contact is ACTIVE, pay it by referencing destination.contact.externalId and the linked externalAccount — see Pay via ACH, Scenario 1.
Scenario 2: Pay a recipient by mailed check
(save a mailing address)
Use this when you'll pay the recipient with a physical check — see Pay via Check.
You'll also need: the recipient's name and mailing address. PO Box addresses aren't supported.
Request
POST /v1/customer/id/{id}/contact
{
"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"
}
}Response
{
"resourceName": "contact",
"url": "/v1/customer/id/4225975/contact/id/4019611",
"id": 4019611,
"externalId": "contact-check-002",
"name": "Jane Doe",
"contactType": "INDIVIDUAL",
"status": "ACTIVE"
}Scenario 3: Pay a payee who has a Passport account
(save an internal payee by PPI)
Use this when the payee you're saving already holds a Passport account. Identify them by their Passport Payment ID (PPI) — a built-in identifier for accounts on the platform — instead of bank details. PPI contacts skip external bank validation.
You'll also need: the payee's PPI, verified first via POST /v1/ppi/verify.
Request
POST /v1/customer/id/{id}/contact
{
"externalId": "contact-ppi-003",
"name": "Bright Tech",
"contactType": "BUSINESS",
"legalName": "Bright Tech Inc",
"ppi": "brighttech@ppi"
}Response
{
"resourceName": "contact",
"url": "/v1/customer/id/4225975/contact/id/4019620",
"id": 4019620,
"externalId": "contact-ppi-003",
"name": "Bright Tech",
"contactType": "BUSINESS",
"ppi": "brighttech@ppi",
"status": "ACTIVE"
}Once the contact is ACTIVE, you pay it through the internal transfer flow.
Eng to confirmWhich pay flow uses a PPI / Passport-account contact as the destination (likely an internal book transfer — Pay via BOOK). Confirm and link the correct flow here.
Scenario 4: Pay a recipient abroad
(save an international bank account for international wire)
Use this to pay a recipient outside the US by international wire, where SWIFT and country/currency details are required — see Pay via International WIRE.
You'll also need: the recipient's account number, SWIFT code, country, and accepted currency.
Request
POST /v1/customer/id/{id}/contact
{
"externalId": "contact-intl-004",
"name": "Globex GmbH",
"contactType": "BUSINESS",
"legalName": "Globex GmbH",
"internationalExternalAccount": {
"accountNumber": "DE89370400440532013000",
"swiftCode": "DEUTDEFF",
"country": "DE",
"acceptedCurrency": "EUR"
}
}Response
{
"resourceName": "contact",
"url": "/v1/customer/id/4225975/contact/id/4019631",
"id": 4019631,
"externalId": "contact-intl-004",
"name": "Globex GmbH",
"contactType": "BUSINESS",
"status": "PENDING_VERIFICATION",
"internationalExternalAccount": {
"resourceName": "internationalExternalAccount",
"id": 4019540,
"country": "DE",
"acceptedCurrency": "EUR",
"status": "PENDING_VERIFICATION"
}
}Scenario 5: Send a virtual card
(save the recipient's email)
Use this when you'll issue a virtual card to the recipient; their email receives the card details once the card is issued by your program.
You'll also need: the recipient's email address.
Request
POST /v1/customer/id/{id}/contact
{
"externalId": "contact-vcard-005",
"name": "Sam Lee",
"contactType": "INDIVIDUAL",
"firstName": "Sam",
"lastName": "Lee",
"email": "[email protected]"
}Response
{
"resourceName": "contact",
"url": "/v1/customer/id/4225975/contact/id/4019642",
"id": 4019642,
"externalId": "contact-vcard-005",
"name": "Sam Lee",
"contactType": "INDIVIDUAL",
"email": "[email protected]",
"status": "ACTIVE"
}Track the contact
A contact returned as PENDING_VERIFICATION becomes usable once OFAC and account-validation (EWS) checks finish. Retrieve it with GET /v1/customer/id/{id}/contact/id/{id} and check the status — it moves to ACTIVE when verification clears, or FAILED if it doesn't. PPI-based contacts skip external validation and are ACTIVE on creation. Use the same GET to fetch a contact's details before paying it, or to confirm an update.
If verification fails
A contact moves to FAILED when a check doesn't pass, and the status reason tells you which check it was:
- OFAC screening — the payee couldn't be cleared against the sanctions list.
- Account validation (EWS) — the bank account couldn't be verified.
- PPI verification — the Passport Payment ID couldn't be confirmed. Verify it with POST
/v1/ppi/verifybefore saving the contact.
A FAILED contact can't be used for payouts. Correct the underlying detail (or resolve the compliance issue) and create the contact again. For the specific reason codes and how to resolve each, see Error Codes and Messages and External Account Error codes.
Eng to confirmThe exact
FAILEDreason values for OFAC, account-validation, and PPI failures, and which error-codes page documents each. The breakdown above is the expected behavior — confirm the precise reason strings and codes before publishing.
To confirm with eng
- The exact GET contact response and verification fields (
status, OFAC / account-validation field names) — confirm against a live response. See also the response note in Scenarios above.- Whether contact creation emits a webhook. The Webhook Event Types page lists customer, account, and external-account events; a dedicated contact event isn't confirmed. If one exists, add a "via webhooks" option here.
Request reference
The complete set of fields for the create-contact request. Scenarios above use subsets of these.
Core parameters
| Parameter | Required | Description |
|---|---|---|
name | ✓ | Display name of the contact |
contactType | ✓ | INDIVIDUAL or BUSINESS |
externalId | Recommended | Your own reference ID, also used as the idempotency key — retrying with the same value won't create a duplicate |
firstName | Conditional | Required when contactType is INDIVIDUAL |
lastName | Conditional | Required when contactType is INDIVIDUAL |
legalName | Conditional | Required when contactType is BUSINESS |
ppi | Conditional | Passport Payment ID for an internal payee. Verify first with POST /v1/ppi/verify |
email | Conditional | Required for virtual-card payouts |
Add one destination object below, matching how you'll pay the contact.
Destination: bank account (ACH / wire — domestic)
Use externalAccount to save a third-party domestic bank account. Reusable as the destination for Pay via ACH, Pay via Wire, and the matching pull-funds flows.
| Parameter | Required | Description |
|---|---|---|
externalAccount.accountNumber | ✓ | Bank account number |
externalAccount.routingNumber | ✓ | ACH routing number |
externalAccount.wireRoutingNumber | Optional | Wire routing number |
externalAccount.type | ✓ | SAVINGS or CHECKING |
externalAccount.holderType | ✓ | CONSUMER or CORPORATE |
Destination: mailing address (check)
Use mailingAddress to save a physical destination for Pay via Check. PO Box addresses aren't supported.
| Parameter | Required | Description |
|---|---|---|
mailingAddress.name | ✓ | Name of the recipient |
mailingAddress.addressLine1 | ✓ | Primary street address |
mailingAddress.addressLine2 | Optional | Apartment, suite, or unit |
mailingAddress.city | ✓ | City |
mailingAddress.state | ✓ | Two-character state code |
mailingAddress.zip | ✓ | ZIP or postal code |
mailingAddress.phone | Optional | Contact phone number |
mailingAddress.isPrimary | Optional | Whether this is the primary address for the contact |
mailingAddress.isDefault | Optional | Whether this is the default address |
mailingAddress.usage.isPayorAddress | Optional | Whether to treat this as a payor address |
Destination: international account (international wire)
Use internationalExternalAccount to save a third-party account outside the US for international wires.
| Parameter | Required | Description |
|---|---|---|
internationalExternalAccount.accountNumber | ✓ | Account number |
internationalExternalAccount.swiftCode | ✓ | SWIFT code |
internationalExternalAccount.country | ✓ | Country |
internationalExternalAccount.acceptedCurrency | ✓ | Supported currency |
Destination: virtual card (email)
Use email to set where a virtual card's details are sent once issued by your program.
| Parameter | Required | Description |
|---|---|---|
email | ✓ (PCE-issued virtual cards) | Email to send details of a virtual card issued via PCE |
Validation
When you submit the request, we check that all required fields are present for the contact type and destination. If validation fails, the request is rejected with an error — correct it and resubmit. See HTTP Response Codes.
What we check:
contactTypeis valid (INDIVIDUALorBUSINESS)- Required fields are present for the chosen destination
- PPI is verified before an internal contact is added
- Bank-account contacts may undergo OFAC and account-validation (EWS) checks
Some destinations (for example, MoneyGram) depend on your program's configuration.
Contact Statuses
| Status | Description | Reason |
|---|---|---|
ACTIVE | Contact is available for use | Successfully created |
INACTIVE | Contact is not usable | Disabled or restricted |
PENDING_VERIFICATION | Awaiting validation | External account checks in progress |
FAILED | Contact creation failed | Validation or compliance failure |
Eng to confirm — contact status modelThis table is carried over from the existing page. Confirm whether the contact returns all four as top-level
statusvalues, or whether the verification states (PENDING_VERIFICATION,FAILED) actually live in a verification object in the GET response while the contactstatusis effectivelyACTIVE/INACTIVE. If so, split this into "Contact status" and "Verification status".
Go live
Before you move from sandbox to production, confirm:
- Production API credentials are in place.
- PPI contacts are verified via POST
/v1/ppi/verifybefore you save them. - Your flow waits for a contact to reach
ACTIVEbefore paying it — bank-account and international contacts may sit inPENDING_VERIFICATIONwhile OFAC and account-validation checks run. - You handle the
PENDING_VERIFICATIONandFAILEDstates (don't assume a contact is usable on creation). - Idempotency is tested — retries with the same
externalIddon't create duplicate contacts.
Best Practices
| Practice | Description |
|---|---|
Set a unique externalId | For Treasury, PCE uses the request-body externalId as the idempotency key, so retrying with the same value never creates a duplicate contact. |
| Verify a PPI before adding | Confirms an internal contact is valid before you save it. |
| Use accurate bank details | Prevents payout failures. Don't store your own bank account as a contact — use External Accounts. |
| Reuse contacts for repeat payments | Faster initiation, fewer input errors, cleaner reconciliation. |
Store the contact id | Required for paying, fetching, updating, or deleting the contact. |
| Avoid PO Box addresses | Not supported for mailing-address contacts. |
Next steps
Now that the contact is saved, pay it.
See also
- Pay via Check — mail a check to a saved address
- Pay via International WIRE — pay an international contact
- External Accounts — save your own bank account, not a third-party contact
- Error Codes and Messages — resolve a failed or rejected contact