Saved Contacts

Saved Contacts store reusable payment instructions for Direct Transfers and Auto Convert Routes. Build their displayFields from the selected route’s metadata.

Required owner, bank, wallet, reference, and memo fields vary by currency and payment rail. Do not keep a separate hardcoded form for each rail.

Create or update a contact

POST /saved-contacts/v1 UpsertSavedContactCommandV1

Omit id to create a contact. Include an existing contact id to update it.

Crypto wallet

{
	"alias": "Treasury USDC wallet",
	"isSavedByUser": true,
	"instruction": {
		"kind": "SavedContact",
		"paymentRail": "Base",
		"currency": "Usdc",
		"displayFields": [
			{
				"identifier": "PrimaryIdentifier",
				"label": "Wallet address",
				"value": "0x123456789abcdef0123456789abcdef01234567",
				"canCopy": true
			}
		]
	}
}

SEPA individual account

{
	"alias": "Alex EUR account",
	"isSavedByUser": true,
	"instruction": {
		"kind": "SavedContact",
		"paymentRail": "Sepa",
		"currency": "Eur",
		"displayFields": [
			{
				"identifier": "PrimaryIdentifier",
				"label": "IBAN",
				"value": "ES7921000813610123456789",
				"canCopy": true
			},
			{
				"identifier": "AccountOwnerType",
				"label": "Beneficiary type",
				"value": "Individual",
				"canCopy": false
			},
			{
				"identifier": "AccountOwnerFirstName",
				"label": "First name",
				"value": "Alex",
				"canCopy": false
			},
			{
				"identifier": "AccountOwnerLastName",
				"label": "Last name",
				"value": "Rivera",
				"canCopy": false
			}
		]
	}
}

SEPA business account

Replace the individual owner fields with:

[
	{
		"identifier": "AccountOwnerType",
		"label": "Beneficiary type",
		"value": "Business",
		"canCopy": false
	},
	{
		"identifier": "AccountOwnerBusinessName",
		"label": "Business name",
		"value": "Example Company S.L.",
		"canCopy": false
	}
]

Expected response:

{
	"contact": {
		"id": "123e4567-e89b-12d3-a456-426614174001",
		"alias": "Treasury USDC wallet",
		"isSavedByUser": true,
		"instruction": {
			"kind": "SavedContact",
			"paymentRail": "Base",
			"currency": "Usdc",
			"displayFields": [
				{
					"identifier": "PrimaryIdentifier",
					"label": "Wallet address",
					"value": "0x123456789abcdef0123456789abcdef01234567",
					"canCopy": true
				}
			],
			"notices": []
		}
	}
}

List compatible contacts

QUERY /saved-contacts/v1 GetSavedContactListQueryV1

For a simple currency and rail filter:

curl https://api-preview.bridflow.com/saved-contacts/v1   --request QUERY   --header 'Content-Type: application/json'   --header 'Authorization: Basic <BASE64(uuid:password)>'   --header 'AccountId: 123e4567-e89b-12d3-a456-426614174000'   --data '{
    "currency": "Usdc",
    "paymentRail": "Base",
    "isSavedByUser": true
  }'

When selecting a contact for a route, also send the complete route and set routeSide to Source or Destination. This lets the backend filter contacts by the actual route contract instead of currency and rail alone.

You can also send savedContactIds to resolve a known set of contacts.

Field identifiers

Common identifiers include:

  • PrimaryIdentifier: wallet address, IBAN, account number, or the rail’s main identifier.
  • SecondaryIdentifier: routing number or another paired identifier.
  • AccountOwnerType, AccountOwnerFirstName, AccountOwnerLastName, and AccountOwnerBusinessName.
  • RecipientName and InstitutionName.
  • Reference and Memo, only when returned by route metadata.

Validation rules

  • Keep identifiers distinct; do not merge IBAN, CLABE, account number, and wallet address into one normalized value.
  • Validate a blockchain address for its selected network before submission.
  • Do not add or silently change a memo. A memo that is not supported by the route must be omitted.
  • Keep provider ids and opaque route fields out of user-facing labels.
  • On an unsupported-route error, refresh discovery and metadata before rebuilding the form.