Explore our documentation to learn how to quickly and easily build a FinTech app or embedded banking product with Synctera.
## Get started
Create a Synctera account to get your API key, access our sandbox, and explore the rest of our Console.
Sign up
## Explore our documentation
Learn everything you need to know about integrating with our APIs and how to make your use case come to life.
View the API Guides
Everything you need to make your project come to life can be found here.
View the APIs
## Dive into our APIs
Creates and manages records for personal customers
Creates and manages records for business customers
Creates and manages customer accounts
Issues, activates, and manages cards for customers
Transfer funds between two accounts in real-time
Create and manage scheduled payments for customers
Issue cards, accept payments, and transfer money between accounts
Handles remote deposit capture (RDC) transactions
Create and manage documentation for a customer and run verification checks
Verify documentation for a customer
Handles legally required disclosures to customers
# ACH Payments
Source: https://docs.synctera.com/v2/docs/ach-guide
If you’re building a service that will enable your customers to send and accept payments, issue cards, or transfer money between accounts at different institutions, chances are you’ll need ACH. ACH is a one-stop shop for a variety of payment use cases.
From recurring payments and payroll deposits to online purchases and bill payment, ACH offers a fast and flexible way to send money.
Originally established in 1970, expanding support and innovation around ACH payments has led it to become one of the most popular online payment rails in the world.
## What is an ACH Transaction?
An ACH transaction (often referred to as an ACH transfer) is an electronic, bank-to-bank money transfer processed through the Automated Clearing House (ACH) Network. You can think of the ACH Network as a postal service for ACH files. Each file can contain up to 1000 ACH entries with each entry representing a transaction being sent from one bank to another.
ACH transactions can be used to facilitate Person-to-person (P2P), Business-to-consumer (B2C), and Business-to-business (B2B) payments and is a convenient alternative to card networks, wire transfers, paper checks or cash. In addition to their convenience, ACH transactions are reliable, inexpensive, fast, and most importantly, safe. Most transfers are processed and settled within 1-2 business day with zero cost to the consumer.
See our [ACH Transfers - Risk and Compliance](/docs/ach-payments-risk-and-compliance) guide for more details on risks and controls, account validation, and NACHA requirements surrounding return rates.
## What are the 2 types of ACH Transactions?
Each ACH transaction involves 2 primary parties:
1. The **Originating Depository Financial Institution (ODFI)** which is the bank initiating the transaction; and
2. The **Receiving Depository Financial Institution (RDFI),** the bank receiving the transaction.
All transactions fall into one of two categories, debit (“pull”) or credit (“push”).
### Push (Credit)
Determining if a transaction is a push or pull is always with respect to the receiving account. For example, if Tom, a customer of Bank A wants to send money to John, a customer of Bank B. Tom’s bank can originate an ACH push transaction from his account to John’s account. The term “push” is used interchangeably with “credit”. The result is a credit to John’s (the receiver) account balance and a debit to Tom’s (the sender).
A real life use case for push transactions are the deposits that result from an employee signing up for payroll direct deposit. Whenever payday rolls around, the employer’s bank initiates a push/credit to the employee’s bank account.
### Pull (Debit)
Alternatively, if Tom want’s to request money from John, Tom’s bank can originate an ACH pull request from his account to John’s. The term “pull” is used interchangeably with “debit”. The result is a debit to John’s account and a credit to Tom’s. This is what occurs when users of a service sign up for recurring bill payments. The service provider ends up debiting the customer’s account via an ACH pull request.
This is also the case when a banking customer deposits a check made out to him/her by an account holder at another financial institution. Checks are ultimately translated into pull request on the ACH network. The check contains the account holders routing and account number which are the only pieces of information required to initiate a credit or debit from an account at another institution.
There is a difference between the direction of an ACH transaction and the direction of money flow. The direction of the transaction refers to the direction of the ACH message on the network. It always flows from the originator to the receiver while the direction of money flow relates to which account is receiving funds and which account is sending them. This means there are 2 ways to achieve the same direction of money movement.
## Scenario
The desired result is to withdraw funds from an account at **Bank A** and deposit them into an account at **Bank B**
### Option 1 (This is how customers fund their accounts)
Bank B sends an ACH **debit / pull** transaction requesting funds from the account at Bank A:
### Option 2
Bank A sends an ACH **credit / push** transaction to send funds to the account at Bank B.
## How does Synctera support ACH?
From the outside, the ACH workflow may seem simple, but it’s actually quite complex. This is where Synctera comes in. Synctera works with your sponsor bank to serve as your connection to the ACH network and supports sending and accepting ACH transactions from other financial institutions.
In ACH terms, Synctera acts as the **ODFI** (Originating Depository Financial Institution) when it comes to originating transactions and the **RDFI** (Receiving Depository Financial Institution) when it comes to receiving them. It means that we take care of all the batch processing and file generation while ensuring that all transactions are reflected in your customer account balances.
### FinTech in the Auth Flow
Synctera allows the FinTech to participate in the transaction authorization decision for Inbound ACH Direct Debit.
This capability can only be enabled for linked zero balance accounts and can be used to:
* Restrict payments to certain institutions
* Run your own balance checks against the linked balance carrying account
If the transaction is declined by the FinTech, then an ACH return will be sent to the network.
The FinTech does not have to participate unless there is an additional approval logic. If the FinTech opts not to participate in the Auth Flow, Synctera will use default authorization logic to authorize the transactions.
In order to participate in Auth Flow for Pull (Debit) ACH transactions, the FinTech should support an **Authorization Gateway** and a corresponding **Gateway Endpoint** must be configured on the Synctera side.
#### Authorization Gateway
An authorization Gateway enables a FinTech to optionally take part in the decision of a Pull (Debit) ACH transaction’s authorization cycle. The FinTech receives an authorization request via the configured Gateway to either **approve** or **decline** corresponding ACH transactions based on the FinTech's own business logic.
**Request Body**
Synctera sends such information as a `POST` HTTP request to the FinTech about Pull (Debit) ACH transactions that needs to be authorized:
```json JSON theme={"system"}
{
"customer_id": "2b9cc6f2-d0bd-4d9d-aa20-5e53355f9469",
"account_id": "0221e0a7-7774-48a4-8521-e678ec09a53a",
"transaction_id": "9b59fc80-9bf5-4749-8dd2-511f183becf2",
"settlement_date": "2022-03-25",
"effective_date": "2022-03-25",
"transaction_type": "incoming_debit",
"dc_sign": "debit",
"amount": 100,
"currency": "USD",
"network": "ACH",
"company_entry_description": "ACCTVERIFY",
"company_name": "PAYPAL"
}
```
**Response HTTP code**
To signal an authorization request decision, the FinTech must reply with the appropriate HTTP code:
* HTTP code `200`: **approve** the corresponding ACH transaction
* HTTP code `402`: **decline** the corresponding ACH transaction
Receiving any other HTTP code in the response will stop the processing of the corresponding Pull (Debit) ACH transaction. However, in this case the transaction will ***not be declined***, but will be ***retried within 2 days*** until the FinTech responds with either `200` or `402` response.
**Response timeout**
Synctera waits for the authorization response within the timeout window which **defaults to 1.5 seconds**, but can be configured in the **Gateway Endpoint Configuration**.
If the response is not received within the defined timeout window, the corresponding Pull (Debit) ACH transaction will stop being processed. But will ***not be declined*** and will be ***retried with exponential backoff*** until the FinTech sends the response within the defined timeout window. If no response is given within the retry window (default: 35 minutes, configurable per bank/partner via `auth_retry_config`), the ACH will be sent to suspense for a manual review by Payment Operations.
#### Gateway Endpoint Configuration
To configure a Gateway for the FinTech, a valid publicly accessible `url` must be provided.
Additionally, `custom_headers` and custom response timeout in milliseconds `max_wait_ms` can be configured. Also, Gateways may be disabled by setting the `disabled` field to `true`.
```sh Shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H "Content-Type: application/json" \
$baseurl/v0/ach/gateways \
--data-binary '
{
"url": "https://example-fintech.com/ach/auth",
"custom_headers": {
"key1": ["value1", "value2"],
"key2": ["value"]
},
"max_wait_ms: 1500,
"disabled": false
}'
```
## How are accounts funded using ACH?
Once your customers have an account setup, they’ll need to fund their account by originating an ACH pull/debit transaction to be sent to their account at another institution. Synctera refers to this as an [external account](/v2/docs/external-accounts-guide).
If you have just created account B on the Synctera platform and you want to add funds to it from account A at another financial institution, its natural to assume that funding involves *pushing* funds from account A to account B. Instead, funding your customers account involves originating an ACH debit/pull transaction with your Synctera account in order to withdraw funds from the external account.
## How do I get started with sending/receiving ACH payments?
### Step 1: Customer Creation
1.1 **Create a Customer**
Any individual wanting to transact on the Synctera platform must be stored as a customer in our system. Part of that includes completing the KYC process. See the [Customer guide](/v2/docs/create-a-personal-customer) for details.
### Step 2: Account Creation
2.1 **Create an Account**
In order to send and receive ACH payments you will need to create an account and associate it with your customer. [Account Guide](/v2/docs/create-accounts-guide)
2.2 **Create an External Account**
In order for your customers to fund their accounts they’ll need to debit funds from an account at another institution. You'll need to create an external account on the Synctera platform which serves as the internal representation of the account to be debited. [External Account Guide](/v2/docs/external-accounts-guide)
### Step 3: Send/Receive an ACH
3.1 **Send an ACH request**
Once the external account has been verified, ACH transactions (credits and debits) can be originated from your customer’s account. If you’re unsure what values to set in your API request take a look at the documentation [here](/v2/reference/addtransactionout).
3.2 **Receive an ACH transaction**
As long as your customer has an account that is both active and verified, there is no work required by you or your customer to receive ACH transactions. Synctera will process the incoming transaction and debit/credit the account accordingly.
## What happens after I send an ACH request?
After you send a request to originate an ACH transaction, the transaction is placed in a batch which, in turn, is placed in an ACH file to be sent out over the network. Depending on the time that you originate the transaction there may be some delay between the time the transaction is created and the time the file is sent. This is done to align with the [processing schedule](https://www.frbservices.org/resources/resource-centers/same-day-ach/fedach-processing-schedule.html) set by the federal reserve. The date the transaction is sent will ultimately determine when funds are settled across accounts and institutions. It will also have a bearing on if and when an ACH transaction can be returned by the recipient.
The responsibilities and guidelines for participants in the ACH system are determined by the National Automated Clearing House Association, better known as NACHA. NACHA operates as the rule making body for all financial institutions wanting to use the ACH system.
## What do I need to do to receive an ACH transaction?
Nothing. Unless an incoming transaction needs to be returned (see [Why would an ACH transaction be returned?](#why-would-an-ach-transaction-be-returned)), no action is required. Synctera will process all debit and credits for any ACH transactions that we receive on behalf of you or your customers. Check your Synctera dashboard to view all ACH transaction that have been sent or received. Synctera also supports webhooks to notify you of all ACH transactions.
## How long does it take to process an ACH transaction?
The time it takes for the network to completely process an ACH transaction is dependent on a number of variables. The operating schedule of the federal reserve, the operating schedule of each bank, and the time of day the transaction was created are all key factors in determining when a transaction will settle. “Settle”. Integrators can influence the timing of an ACH transaction by setting the `is_same_day` field to `true` when posting a request to send a transaction.
However, even when sending “same day” ACH transfers, there are a couple things to keep in mind:
* Transactions can be returned up to 2 business days after the settlement day. This introduces risk for fintech’s who choose to make funds immediately available to account owner who have initiated a pull request. The ACH system operates on a “no news is good news” basis. The absence of a return within the 2-day timeframe typically means that the RDFI has accepted the transaction.
* “Same day” ACH is only available within certain transmission windows set by the federal reserve. Take a look at the Fed [processing schedule](https://www.frbservices.org/resources/resource-centers/same-day-ach/fedach-processing-schedule.html) for more information on cut off times. In general, transactions submitted (Mon-Fri) by 4:45pm are eligible to settle on the same day. “Same day” is not available on non-banking days (weekends and holidays) and will automatically default to the next available “banking day”.
* Submitting incorrect information may cause processing errors that result in delays. Errors may also increase the likelihood of a transaction being returned.
## Why would an ACH transaction be returned?
ACH transactions can be returned for a variety of reasons. “Insufficient funds”, “invalid account number”, “account closed” are all fair game but but many of the return reasons are not so obvious. NACHA guidelines specify [85 different codes](https://engineering.gusto.com/how-ach-works-a-developer-perspective-part-2/#appendix) that may be associated with a returned transaction. The good news is that you and your customers are likely to see only the most common ones. Synctera will take care of processing any returned transaction and notify you of the resolution via webhooks. In the event that a returned transaction requires your immediate attention, you will be able to take action through the Synctera dashboard.
The dashboard is also were you can generate returns from received ACH transactions. While most transaction failures such as insufficient funds, invalid account, etc., will result in automatic returns, you may occasionally identify transactions that were not authorized and choose to manually initiate a return.
## ACH same day vs non-same day cut-off times
| ACH Type | Cut-off time | Comments |
| ------------ | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Same day | 4:45 pm ET (1:45 pm PT) (Monday - Friday) | Same day ACH payments with effective date of today submitted before 4:45 pm ET will be processed as ‘same day’ ACH |
| Non-Same day | 24:00 pm ET (Monday-Friday) | Non same ACH payments with effective date of tomorrow or 2 days in the future submitted before 24:00 will be processed as ‘non-same day’ |
**ACH (Debit/Credit) - External bank to Synctera account**
| ACH Type | File processing time | Comments |
| ------------------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| Same day | 10:30 am ET2:45 pm ET4:45 pm ET | ACH Batch files with same day payments (debits/credits/returns) directed to Synctera accounts will be processed at these times |
| Non same day / future dated ACH | 10:30 am ET2:45 pm ET4:45 pm ET8:00 pm ET \*2:15 am ET \* | ACH Batch files with non-same day payments (debits/credits/returns) directed to the Synctera accounts will be processed at these times |
Not all of our sponsor banks support 8:00 pm ET / 2:15 am ET exchange times
**ACH Returns - External bank to Synctera account**
| Return code | Days from original transaction |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| **R01:** NSF | 2 Banking Days |
| **R02:** Account closed | 2 Banking Days |
| **R03:** No Account - Account number structure is valid, but doesn't match individual or Open account | 2 Banking Days |
| **R04:** Invalid Account - Account number structure not valid, ie edit check digit or number failed | 2 Banking Days |
| **R05:** Unauthorized Debit to Consumer Account Using Corporate SEC Code - A Debit entry that uses a corporate SEC code was transmitted to a consumer but was not authorized by the consumer | 60 Calendar Days |
| **R07:** Authorization Revoked - Customer who previously authorized an entry claims, authorization has been revoked from the Originator | 60 Calendar Days |
| **R08:** Payment Stopped: The customer has requested the stop payment of a specific ACH Debit Entry | 2 Banking Days |
| **R09:** Sufficient ledger balance exists, but value of uncollected items brings available balance below amount of debit entry | 2 Banking Days |
| **R10:** Customer Advises Not Authorized, Improper, Ineligible, Part of Incomplete transaction or Improperly reinitiated - Not authorized, wrong amount, debit date before authorized, incomplete transaction, improper source document or exceeds reinitiating attempt | 60 Calendar Days |
| **R12:** Account sold to another FI | 2 Banking Days |
| **R16:** Account frozen/Entry Returned Per OFAC Instruction - Access to account is restricted due to action by the bank | 2 Banking Days |
| **R20:** Non-transaction Account - Policies and regulations restrict activity to account indicated | 2 Banking Days |
| **R24:** Duplicate Entry - Entry is a duplication. The trace number, date, dollar amount, etc. match another entry | 2 Banking Days |
| **R29:** Corporate Customer Advises Not Authorized | 2 Banking Days |
| **R31:** Permissible Return Entry - Sender bank agreed on behalf of the Originator to accept a return after the deadline for an unauthorized corporate entry | Undefined |
| **R37:** Source document Presented for Payment - The source document to which an ARC or BOC or POP entry relates has also been presented for payment | 60 Calendar Days |
| **R38:** Stop Payment of Source Document - A Stop Payment has been placed on the source document to which the ARC or BOC Entry relates. | 60 Calendar Days |
| **R39:** Improper Source Document/Source Document presented for payment - The RDFI determines the source document for the ARC, BOC or POP entry is not an eligible item or was presented for payments | 2 Banking Days |
| **R50:** State Law Affecting RCK Acceptance - RDFI is located in a state that has not adopted Revised Article 4 of the UCC or RDFI is located in a state that requires all canceled checks to be returned to the receiver | 2 Banking Days |
| **R51:** Item is Ineligible, Notice Not Provided, Signatures not Genuine, Item Altered or Amount of RCK Not Accurately Obtained from the Item | 60 Calendar Days |
| | |
## Company Entry Description
As of March 20, 2026, NACHA dictates that ACH transactions meeting the requirements for Payroll or Purchase transactions are required to be classified in the Company Entry Description as such:
`PAYROLL`: wages, salary, or other similar types of compensation for labor or services. `PURCHASE`: e-commerce purchases authorized by a consumer receiver for the online purchase of goods. This does not include recurring bills such as utilities, mortgages or other types of bill payments.
If sending a Payroll or Purchase payment via Synctera ACH, include the `PAYROLL` or `PURCHASE` descriptor in the `company_entry_description` field accordingly.
## Example: Funding a customer account
To fund a customer account from an external bank account the Synctera API:
```shell shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H "Content-Type: application/json" \
https://api.synctera.com/v0/ach \
--data-binary '
{
"risk": {
"client_ip": "127.0.0.1"
},
"customer_id": "{CUSTOMER_ID}",
"amount": 100,
"currency": "USD",
"receiving_account_id": "{EXTERNAL_ACCOUNT_ID}",
"dc_sign": "debit",
"originating_account_id": "{ACCOUNT_ID}",
"reference_info": "Synctera test",
"memo": "Synctera test"
}'
```
It is important to note a few things in this example:
1. The `receiving_account_id` always refers to an [External Account](/v2/docs/external-accounts-guide).
2. The `originating_account_id` always refers to to an [Account](/v2/docs/create-accounts-guide).
3. The `dc_sign` is always from the perspective of the **receiving** account. So a `debit` to the receiving account results in a credit to the originating account.
4. The `amount` is always in the smallest denomination of the given currency. In this case, the currency is `USD`, which means the amount is in cents.
5. The `customer_id` indicates the person that is making the outgoing ACH request. This customer must be an account holder or authorized signer of `originating_account_id`
# Adverse Actions
Source: https://docs.synctera.com/v2/docs/adverse-actions-guide
Adverse action notices record and communicate the reasons a credit decision adversely affected a customer, as required by the ECOA and FCRA.
## Overview
Adverse action notifications are a cornerstone of regulatory compliance in financial services. Mandated by the Equal Credit Opportunity Act (ECOA) and the Fair Credit Reporting Act (FCRA), they ensure applicants are informed promptly and transparently when a decision adversely affecting them is made. The Adverse Actions API streamlines recording these notices and associating them with the application or account they concern.
**An adverse action** captures the reasons a customer was denied credit — or offered less favorable terms than requested — along with the purpose of the decision and the resource it relates to.
Key characteristics:
* **Reason-bearing** — a notice carries up to four principal `reasons` (a fifth may be required if one reason is inquiry-related).
* **Purpose-scoped** — the `purpose` records the point in the account lifecycle the decision was made (e.g. `ACCOUNT_OPENING`, `ACCOUNT_CLOSURE`).
* **Associated** — every notice is tied to an application or account via `related_resource_id` and `related_resource_type`.
* **Timely** — the notice of adverse action (NOAA) must generally be delivered within 30 days of the credit decision.
A fintech must give customers specific reasons when credit is denied or terms are less favorable than requested — typically up to four principal reasons, plus a fifth if one is inquiry-related. Adverse actions apply across the credit lifecycle: account opening denial, denied line-increase request, penalty-based APR increase, account closure, and more.
## Prerequisites
This guide assumes you are familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
* Credit [applications](/v2/reference/patchapplication) and accounts
## The adverse action object
| Field | Description |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `id` | Unique identifier (read-only, assigned on creation). |
| `purpose` | The lifecycle event the decision concerns (e.g. `ACCOUNT_OPENING`, `ACCOUNT_CLOSURE`). |
| `reasons` | The principal reasons for the adverse action (e.g. `TOO_MANY_INQUIRIES`, `INSUFFICIENT_CREDIT_HISTORY`). |
| `related_resource_id` | The `id` of the associated customer or account. |
| `related_resource_type` | The type of associated resource: `CUSTOMER` or `ACCOUNT`. |
| `notification_time` | When the customer was notified of the decision. |
| `creation_time` / `last_updated_time` | Timestamps (read-only). |
```json theme={"system"}
{
"id": "2fb2858b-f859-4dc8-9ad2-2a4e596fed89",
"purpose": "ACCOUNT_OPENING",
"reasons": ["TOO_MANY_INQUIRIES", "INSUFFICIENT_CREDIT_HISTORY"],
"related_resource_id": "9337a443-fa03-471c-ab05-b138c41dbd17",
"related_resource_type": "CUSTOMER",
"notification_time": "2020-05-19T21:14:27.434964Z",
"creation_time": "2023-09-19T15:48:24.10184Z",
"last_updated_time": "2023-09-19T15:48:24.10184Z"
}
```
See the [API reference](/v2/reference/createadverseaction) for the full schema.
## Recording adverse actions
### Example: a denied Line of Credit application
When an application to open a Line of Credit account is denied, record the notice with [POST /v2/adverse\_actions](/v2/reference/createadverseaction), scoped to the customer:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/adverse_actions \
--data-binary '
{
"notification_time": "2020-05-19T21:14:27.434964Z",
"purpose": "ACCOUNT_OPENING",
"reasons": ["TOO_MANY_INQUIRIES", "INSUFFICIENT_CREDIT_HISTORY"],
"related_resource_id": "9337a443-fa03-471c-ab05-b138c41dbd17",
"related_resource_type": "CUSTOMER"
}'
```
The response includes the system-generated `id`:
```json theme={"system"}
{
"id": "2fb2858b-f859-4dc8-9ad2-2a4e596fed89",
"purpose": "ACCOUNT_OPENING",
"reasons": ["TOO_MANY_INQUIRIES", "INSUFFICIENT_CREDIT_HISTORY"],
"related_resource_id": "9337a443-fa03-471c-ab05-b138c41dbd17",
"related_resource_type": "CUSTOMER",
"notification_time": "2020-05-19T21:14:27.434964Z",
"creation_time": "2023-09-19T15:48:24.10184Z",
"last_updated_time": "2023-09-19T15:48:24.10184Z"
}
```
Associate the adverse action with the applicant on the Line of Credit application using [PATCH /v2/applications/\{APPLICATION\_ID}](/v2/reference/patchapplication), and move the application to `CREDIT_DENIED`:
```shell theme={"system"}
curl \
-X PATCH \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/applications/{APPLICATION_ID} \
--data-binary '
{
"applicants": [
{
"adverse_action_id": "2fb2858b-f859-4dc8-9ad2-2a4e596fed89",
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"is_primary": true
}
],
"status": "CREDIT_DENIED"
}'
```
```json theme={"system"}
{
"id": "ebda67f0-e0a7-41e2-98ed-0617a1e815a6",
"account_type": "LINE_OF_CREDIT",
"type": "CREDIT",
"purpose": "ACCOUNT_OPENING",
"status": "CREDIT_DENIED",
"applicants": [
{
"adverse_action_id": "2fb2858b-f859-4dc8-9ad2-2a4e596fed89",
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"is_primary": true
}
],
"creation_time": "2022-10-26T19:14:45.861687Z",
"last_updated_time": "2023-09-20T00:31:10.255042Z"
}
```
### Example: an account closed for delinquency
When a charge-secured account is closed due to delinquency, record the notice scoped to the account with `related_resource_type` set to `ACCOUNT`:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/adverse_actions \
--data-binary '
{
"notification_time": "2020-05-19T21:14:27.434964Z",
"purpose": "ACCOUNT_CLOSURE",
"reasons": ["FRAUDULENT_ACTIVITIES"],
"related_resource_id": "9337a443-fa03-471c-ab05-c538c41dbd17",
"related_resource_type": "ACCOUNT"
}'
```
```json theme={"system"}
{
"id": "2fb2858b-f859-4dc8-9ad2-3b4e596fed89",
"purpose": "ACCOUNT_CLOSURE",
"reasons": ["FRAUDULENT_ACTIVITIES"],
"related_resource_id": "9337a443-fa03-471c-ab05-c538c41dbd17",
"related_resource_type": "ACCOUNT",
"notification_time": "2020-05-19T21:14:27.434964Z",
"creation_time": "2023-09-19T15:48:24.10184Z",
"last_updated_time": "2023-09-19T15:48:24.10184Z"
}
```
## Best practices
The notice of adverse action must generally be delivered within **30 days** of the credit decision. Record the adverse action and notify the customer promptly to stay within the regulatory window.
* **Limit to principal reasons** — provide up to four principal reasons, adding a fifth only when one reason is inquiry-related.
* **Always associate the notice** — link every adverse action to its application (`ACCOUNT_OPENING`) or account (`ACCOUNT_CLOSURE`, APR increase, etc.).
* **Set an accurate `notification_time`** — it anchors the compliance timeline; record when the customer was actually notified.
* **Cover the full lifecycle** — record adverse actions for line-increase denials and penalty APR increases, not just openings and closures.
## Related guides
The customer an adverse action is scoped to.
Link adverse actions to the applications they concern.
The credit accounts adverse actions apply to.
## API reference
* [Create an adverse action](/v2/reference/createadverseaction)
* [List adverse actions](/v2/reference/listadverseactions)
* [Get an adverse action](/v2/reference/getadverseaction)
* [Update an application](/v2/reference/patchapplication)
# Applications
Source: https://docs.synctera.com/v2/docs/applications-guide
The Application API is used to collect applicants' data. As FinTech processes the application, they update the content and status of the application.
For different use cases, Synctera supports distinct types of applications. The current application types are:
1. Credit Applications: Used for credit products where the end-user is a consumer or a business. See details [here](/v2/docs/credit-applications-guide).
2. Restricted Account Application: Application to process special cases of Business accounts.
# Balance Floor and Ceiling and Linked Accounts
Source: https://docs.synctera.com/v2/docs/balance-floor-ceiling
Accounts can have a minimum and maximum balance, referred to as the balance floor and balance ceiling. These attributes can be used to implement a number of features, including pool accounts.
Balance Floor and Ceiling can be set while creating an account using [`POST /v0/accounts`](/v2/reference/createaccount)
## Balance Floor
An account's balance floor consists of two attributes:
* `balance`: the minimum balance the account can have
* `linked_account_id`: the ID of the linked account used to maintain the minimum balance
The balance floor creates a lower limit for the available balance of that account. Any transaction that would put the available balance of the account below its balance floor triggers a just-in-time (JIT) funding transaction from the linked account in order to bring the balance back up to the floor.
## Balance Ceiling
The balance ceiling creates an upper limit for the available balance. Any transaction that would put the available balance of the account above its balance ceiling triggers a just-in-time (JIT) funding transaction (also known as a sweep transaction) to the linked account in order to bring the balance back down to the ceiling.
## Example Use Cases
### Pool Account
There are a number of use cases where you want an account that always has a zero balance, and gets its funds from another account. For example, you many have a single pool account containing the funds for multiple business expense cards. In this case, each card's account would be set up like:
```json JSON theme={"system"}
balance_floor: {
"balance": 0,
"linked_account_id": "{POOL_ACCOUNT_ID}"
}
balance_ceiling: {
"balance": 0,
"linked_account_id": "{POOL_ACCOUNT_ID}"
}
```
All credits and debits to any of the card accounts would flow through to the pool account. Refer to the [Business Expense Cards](/v2/docs/business-card) for more details.
# Bank Migration Guide
Source: https://docs.synctera.com/v2/docs/bank-migration
Use the Migration Mapping API to link old and new resources when migrating from one sponsor bank to another on the Synctera platform.
When a FinTech migrates from one sponsor bank to another on the Synctera platform, resources such as customers, accounts, and external accounts must be recreated under the new bank's tenant. The **Migration Mapping API** provides a persistent record of the relationship between an old resource and its newly created counterpart, bridging the two tenants throughout the migration and afterward.
Migration mappings do not move data or trigger any automated migration process. They serve two key purposes:
* **Operational continuity** — FinTech operators, Synctera support teams, and bank partners can navigate between old and new records in the Synctera dashboard during and after a migration.
* **Behind-the-scenes integrations** — Synctera uses mappings internally to coordinate activities that require knowledge of both the old and new resource, such as transferring a credit-reporting tradeline from the old bank to the new one without disrupting the customer's credit history.
## Prerequisites
Before using the Migration Mapping API you should be familiar with:
* [Create a Personal Customer](/v2/docs/create-a-personal-customer)
* [Create a Business Customer](/v2/docs/create-a-business)
* Accounts — creating deposit and other account types
You will need:
* An API key for your **existing** (old) tenant
* An API key for your **new** tenant
* The resource IDs of records you intend to migrate (customers, accounts, etc.)
## The migration mapping object
A migration mapping represents the link between one specific resource on the old tenant and its newly created equivalent on the new tenant.
```json theme={"system"}
{
"id": "191edb33-3fca-4c68-8ca5-871fa0d5e3f5",
"tenant": "456",
"resource_id": "def45678-0000-0000-0000-000000000002",
"old_tenant": "123",
"old_resource_id": "abc12345-0000-0000-0000-000000000001",
"resource_type": "PERSON",
"creation_time": "2026-03-01T10:00:00Z",
"last_updated_time": "2026-03-01T10:00:00Z"
}
```
| Field | Description |
| ------------------- | ---------------------------------------------------------------------------- |
| `id` | Unique identifier for the migration mapping |
| `tenant` | Tenant ID of the **new** bank |
| `resource_id` | ID of the resource in the **new** tenant |
| `old_tenant` | Tenant ID of the **old** bank |
| `old_resource_id` | ID of the resource in the **old** tenant |
| `resource_type` | The type of resource being mapped. One of `PERSON`, `BUSINESS`, or `ACCOUNT` |
| `creation_time` | ISO 8601 timestamp when this mapping was created |
| `last_updated_time` | ISO 8601 timestamp when this mapping was last updated |
### Cross-tenant access
Migration mappings are stored under the new tenant (`tenant`) but are queryable using an API key from either the old or the new tenant. This allows operators to look up a mapping regardless of which API key they have at hand during the migration process.
### Permissions
Querying and managing migration mappings uses the same permissions as the underlying resource type:
| `resource_type` | Required permission |
| --------------- | ---------------------------------- |
| `PERSON` | `customer:read` / `customer:write` |
| `BUSINESS` | `customer:read` / `customer:write` |
| `ACCOUNT` | `account:read` / `account:write` |
## Example: Migrate customers and accounts to a new bank
The following example walks through the complete workflow for migrating personal customers and their associated accounts from an old tenant to a new tenant.
Start by retrieving the list of customers to be migrated from your existing tenant. In this example, the old tenant ID is `123` and the new tenant ID is `456`.
```shell theme={"system"}
curl -X GET https://api.synctera.com/v2/persons \
-H "Authorization: Bearer $old_apikey"
```
The response returns a paginated list of person objects. Record the `id` of each person you intend to migrate.
```json theme={"system"}
{
"persons": [
{
"id": "abc12345-0000-0000-0000-000000000001",
"first_name": "Jane",
"last_name": "Smith",
"email": "jane.smith@example.com"
}
],
"next_page_token": "..."
}
```
For each customer, retrieve their accounts so you have the full picture of what needs to be recreated.
```shell theme={"system"}
curl -X GET "https://api.synctera.com/v2/accounts?customer_id=abc12345-0000-0000-0000-000000000001" \
-H "Authorization: Bearer $old_apikey"
```
Record the `id` of each account alongside its owner's person ID.
Using the information collected above, create each customer on the new tenant. Submit the same personal information as was on record under the old tenant.
```shell theme={"system"}
curl -X POST https://api.synctera.com/v2/persons \
-H "Authorization: Bearer $new_apikey" \
-H "Content-Type: application/json" \
--data-binary '{
"first_name": "Jane",
"last_name": "Smith",
"dob": "1985-06-15",
"email": "jane.smith@example.com",
"phone_number": "+14155551234",
"legal_address": {
"street_line_1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94105",
"country_code": "US"
}
}'
```
The response includes the new `id` for the person on the new tenant. Record this value — you will need it when creating the migration mapping.
```json theme={"system"}
{
"id": "def45678-0000-0000-0000-000000000002",
"first_name": "Jane",
"last_name": "Smith"
}
```
Repeat this step for each customer in your migration list, keeping a record of the mapping from old IDs to new IDs.
With the new customer IDs in hand, create corresponding accounts on the new tenant. Use the same account configuration (product type, etc.) as the original.
```shell theme={"system"}
curl -X POST https://api.synctera.com/v2/accounts \
-H "Authorization: Bearer $new_apikey" \
-H "Content-Type: application/json" \
--data-binary '{
"account_type": "CHECKING",
"customer_ids": ["def45678-0000-0000-0000-000000000002"]
}'
```
Record the new account `id` returned in the response alongside the old account `id`.
Now that each customer exists on both tenants, create a migration mapping for each person. This call can be made with either the old or the new API key.
```shell theme={"system"}
curl -X POST https://api.synctera.com/v2/migration_mappings \
-H "Authorization: Bearer $new_apikey" \
-H "Content-Type: application/json" \
--data-binary '{
"tenant": "456",
"resource_id": "def45678-0000-0000-0000-000000000002",
"old_tenant": "123",
"old_resource_id": "abc12345-0000-0000-0000-000000000001",
"resource_type": "PERSON"
}'
```
A successful response returns the complete migration mapping object:
```json theme={"system"}
{
"id": "191edb33-3fca-4c68-8ca5-871fa0d5e3f5",
"tenant": "456",
"resource_id": "def45678-0000-0000-0000-000000000002",
"old_tenant": "123",
"old_resource_id": "abc12345-0000-0000-0000-000000000001",
"resource_type": "PERSON",
"creation_time": "2026-03-01T10:00:00Z",
"last_updated_time": "2026-03-01T10:00:00Z"
}
```
Repeat this step for each migrated customer.
Create a corresponding mapping for each account, linking the old account ID to the new account ID.
```shell theme={"system"}
curl -X POST https://api.synctera.com/v2/migration_mappings \
-H "Authorization: Bearer $new_apikey" \
-H "Content-Type: application/json" \
--data-binary '{
"tenant": "456",
"resource_id": "acct9999-0000-0000-0000-000000000004",
"old_tenant": "123",
"old_resource_id": "acct1111-0000-0000-0000-000000000003",
"resource_type": "ACCOUNT"
}'
```
You have now recreated your customers and accounts on the new tenant and established a persistent record linking each pair of old and new resources. Synctera will use these mappings to automatically coordinate any downstream processes that require knowledge of both records, such as transferring credit-reporting tradelines.
## Querying migration mappings
You can look up migration mappings at any time using any combination of the following filters.
### Look up by resource ID
The `resource_id` filter matches against **both** `resource_id` and `old_resource_id`. Use it whenever you have an ID and don't need to constrain which side of the mapping it belongs to — this is the most common lookup pattern.
```shell theme={"system"}
curl -X GET "https://api.synctera.com/v2/migration_mappings?resource_type=PERSON&resource_id=def45678-0000-0000-0000-000000000002" \
-H "Authorization: Bearer $new_apikey"
```
### Look up strictly by the old resource ID
If you specifically want to match only on the old side of the mapping (for example, to find every record that originated from a particular old resource), use `old_resource_id`.
```shell theme={"system"}
curl -X GET "https://api.synctera.com/v2/migration_mappings?resource_type=PERSON&old_resource_id=abc12345-0000-0000-0000-000000000001" \
-H "Authorization: Bearer $old_apikey"
```
## Updating and deleting mappings
### Update a mapping
Use `PATCH` to update mutable fields on an existing mapping (for example, if a resource ID was recorded incorrectly).
```shell theme={"system"}
curl -X PATCH https://api.synctera.com/v2/migration_mappings/191edb33-3fca-4c68-8ca5-871fa0d5e3f5 \
-H "Authorization: Bearer $new_apikey" \
-H "Content-Type: application/json" \
--data-binary '{
"resource_id": "def99999-0000-0000-0000-000000000002"
}'
```
### Delete a mapping
```shell theme={"system"}
curl -X DELETE https://api.synctera.com/v2/migration_mappings/191edb33-3fca-4c68-8ca5-871fa0d5e3f5 \
-H "Authorization: Bearer $new_apikey"
```
Deleting a migration mapping removes the link between the old and new resource. Synctera will no longer be able to automatically coordinate downstream processes (such as credit-reporting tradeline transfers) for the affected resource. Only delete a mapping if you are certain the association is no longer needed.
## Webhooks
Synctera publishes webhook events for migration mapping lifecycle changes. Subscribe to these events to trigger your own automation during a migration.
| Event | Description |
| -------------------------- | ----------------------------------- |
| `RESOURCE_MAPPING.CREATED` | A new migration mapping was created |
| `RESOURCE_MAPPING.UPDATED` | An existing mapping was updated |
| `RESOURCE_MAPPING.DELETED` | A mapping was deleted |
See the [Webhooks guide](/v2/docs/webhooks-guide) for instructions on configuring webhook subscriptions.
## Related guides
Recreate personal customers on the new tenant.
Recreate business customers on the new tenant.
Recreate accounts before mapping old and new IDs.
Subscribe to migration mapping lifecycle events.
## API reference
See the full Migration Mappings API reference for request/response schemas and all available parameters:
* [List migration mappings](/v2/reference/listmigrationmappings)
* [Create a migration mapping](/v2/reference/createmigrationmapping)
* [Get a migration mapping](/v2/reference/getmigrationmapping)
* [Update a migration mapping](/v2/reference/updatemigrationmapping)
* [Delete a migration mapping](/v2/reference/deletemigrationmapping)
## Navigating migrated resources in the dashboard
Once migration mappings exist, the Synctera dashboard surfaces links between old and new records. On any customer or account detail page, an action menu item — **View Migrated Resource** — will appear when a mapping exists. Selecting it navigates directly to the corresponding record on the other tenant, making it easy for FinTech operators, bank partners, and Synctera support to trace a resource through the migration.
## What happens after mappings are created
After you create migration mappings for your customers and accounts, Synctera uses the mappings to handle certain processes automatically on your behalf. You do not need to take additional action for these:
* **Credit-reporting tradeline transfer** — For FinTechs that report to credit bureaus, Synctera will record a sale of the tradeline on the old bank and a corresponding purchase on the new bank, ensuring your customers' credit histories remain uninterrupted.
Other post-migration steps — such as re-running KYC, issuing new cards, and migrating balances — must be completed by your team as part of your broader migration runbook. Work with your Synctera implementation manager for a full migration checklist.
# Bulk Card Orders
Source: https://docs.synctera.com/v2/docs/bulk-card-orders
The Synctera platform supports the issuance of cards in bulk and can be configured for automatic daily fulfillment or fulfillment at your request.
### In order to create bulk orders your card product needs to be configured for bulk issuance.
## Bulk Order Creation Steps
### 1. Create a Bulk Order Configuration
A bulk order configuration provides the shipping instructions for individual bulk orders. Once a bulk order configuration has been set up in the Synctera system, cards can be issued in bulk and will be shipped to the destination specified in the configuration. Fulfillment of the bulk order configuration is based on the bulk issuance policy specified in the configuration.
#### Bulk Issuance Policy
| Bulk Issuance Policy | Description |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AUTO | Bulk orders configured with an AUTO fulfillment policy will be fulfilled nightly at 9:30pm PST (UTC -8:00). All cards that have been issued with the corresponding bulk order config id prior to the cut-off time will be included in the daily bulk order. |
| MANUAL | Bulk orders configured with a MANUAL fulfillment policy will be fulfilled at the request of the integrator. All cards that have been issued with the corresponding bulk order config id prior to the requested fulfillment will be included in the bulk order. |
Call [POST /v1/cards/bulk\_issuance](/v2/reference/createbulkorderconfig) to create a bulk order config.
```sh Shell theme={"system"}
-X POST \
$baseurl/v1/cards/bulk_issuance \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
--data-binary '
{
"name": "Sample Bulk Order Config",
"card_product_id": {CARD_PRODUCT_ID},
"bulk_issuance_policy": "AUTO",
"shipping": {
"recipient_name": {
"first_name": "Jane",
"last_name": "Smith"
},
"address": {
"address_line_1": "123 Main St",
"city": "Seattle",
"state": "WA",
"postal_code": "98109",
"country_code": "US"
},
"method": "INTERNATIONAL"
}
}'
```
This will return a response with the created bulk order configuration:
```json JSON theme={"system"}
{
"id": "7d943c51-e4ff-4e57-9558-08cab6b963c7",
"creation_time": "2010-05-06T12:23:34.321Z",
"name": "Sample Bulk Order Config",
"card_product_id": "7d943c51-e4ff-4e57-9558-08cab6b963c7",
"shipping": {
"recipient_name": {
"first_name": "Jane",
"last_name": "Smith"
},
"address": {
"address_line_1": "123 Main St",
"city": "Seattle",
"state": "WA",
"postal_code": "98109",
"country_code": "US"
},
"is_expedited_fulfillment": false,
"method": "INTERNATIONAL",
},
"bulk_issuance_policy": "AUTO",
"tenant": "abcdef_ghijkl"
}
```
Note the returned `id` attribute. This is used in future card issuance requests in order to include the card as part of a bulk shipment and in order to fulfill bulk orders configured for `MANUAL` fulfillment.
### 2. Issue Cards
Cards are dynamically added to a bulk order by providing the bulk order configuration `id` in the card issuance request.
Call [POST /v1/cards](/v2/reference/issuecard) to issue a card.
### 3. Fulfill Bulk Order
### bulk order configurations with an auto policy are automatically fulfilled nightly at 9:30pm PST (UTC -8:00)
#### Manual Order Fulfillment
Call [POST /v1/cards/bulk\_issuance](/v2/reference/fulfillbulkorder) to fulfill the bulk order.
```sh Shell theme={"system"}
-X POST \
$baseurl/v1/cards/bulk_issuance/{$bulk_order_config_id}/fulfill \
-H "Authorization: Bearer $apikey"
```
This will return a `202 Accepted` response. All cards issued with the corresponding bulk order config id since the last fulfillment request will be included in the bulk order.
#### Card Fulfillment
Once a card has been shipped a webhook will be triggered with the updated shipping details. Including a tracking number if available.
To subscribe to the `CARD.UPDATED` webhook refer to the [Webhooks Guide](/v2/docs/webhooks-guide).
# Business Expense Cards
Source: https://docs.synctera.com/v2/docs/business-card
This guide will show how to set up business expense cards in Synctera's platform.
In this guide we will create a business expense account which will fund card purchases made by the business's employees, subject to spending controls. The purpose of this guide is to give an overview of how the various entities in Synctera's platform work together to be able to handle this type of use case.
## Prerequisites
Before continuing in this guide you will also need a card product set up using a commercial BIN. We will refer to its UUID as `{{card_product_id}}`.
Please also have a business created and fully verified such that is has a `verification_status` of `"ACCEPTED"`. We will refer to the UUID of this business as `{{business_id}}`.
You will also need one or more persons in the system that have been verified and passed KYC. This means they must have a `kyc_status` of `"ACCEPTED"`. We will refer to their UUIDs as `{{employee1_id}}`, `{{employee2_id}}`, etc. Refer to the [KYC Verification API Overview](/v2/reference/verify) for the process to get your customers verified.
## Business expense account
Create an account template by performing a [POST /v0/accounts/templates](/v2/reference/createaccounttemplate) with the following request body.
```json JSON theme={"system"}
{
"name": "business expense accounts",
"is_enabled": true,
"template": {
"account_type": "CHECKING",
"bank_country": "US",
"currency": "USD",
"is_ach_enabled": false,
"is_card_enabled": false,
"is_p2p_enabled": true
}
}
```
Note that cards are not enabled for accounts created using this template but the accounts can be funded via P2P transactions (money movement within Synctera's platform).
The request should return a new account template. We will refer to this template's UUID as `{{business_account_template_id}}`.
Using this new template, create a new account by performing a [POST /v0/accounts](/v2/reference/createaccount) with the following request body.
```json JSON theme={"system"}
{
"account_template_id": "{{business_account_template_id}}",
"relationships": [
{
"relationship_type": "ACCOUNT_HOLDER",
"business_id": "{{business_id}}"
}
]
}
```
We will refer to this new account's UUID as `{{business_account_id}}`. Because we disabled cards in the account template, we can not issue cards for this account directly. The cards will be issued against the employee expense accounts which will in turn be connected to this account.
## Spend control
Create a spend control rule by performing a [POST /v0/spend\_controls](/v2/reference/createspendcontrol) with the following request body. For more details, see the [spend controls guide](/v2/docs/spend-controls-guide).
```json JSON theme={"system"}
{
"name": "Employee monthly limit",
"amount_limit": 100000,
"time_range": {
"time_range_type": "ROLLING_WINDOW_DAYS",
"days": 30
},
"action_decline": true,
"action_case": false,
"is_active": true
}
```
The request should return a new spend control rule. We will refer to this rule's UUID as `{{spend_control_id}}`.
## Employee expense accounts
Create another account template by performing a [POST /v0/accounts/templates](/v2/reference/createaccounttemplate) with the following request body.
```json JSON theme={"system"}
{
"is_enabled": true,
"name": "employee spending accounts",
"template": {
"account_type": "CHECKING",
"bank_country": "US",
"currency": "USD",
"balance_floor": {
"balance": 0,
"linked_account_id": "{{business_account_id}}"
},
"balance_ceiling": {
"balance": 0,
"linked_account_id": "{{business_account_id}}"
},
"is_ach_enabled": false,
"is_card_enabled": true,
"is_p2p_enabled": false,
"spend_control_ids": [
"{{spend_control_id}}"
]
}
}
```
This request references the spend control.
This request also includes balance floor and balance ceiling objects. The balance floor is zero and the `overdraft_account_id` refers to the business account we created earlier. This means that for any account created using this template, the business account will provide real-time funding in order to keep the account's balance at or above zero. Similarly, the balance ceiling is also zero and the `overflow_account_id` also refers to the business account we created earlier. This means that for any account using this template, the business account will receive funds flowing in to the account in order to keep the account's balance at or below zero. In other words, accounts created using this template never hold a balance and card transactions on these accounts effectively move money to and from the business account. The accounts are still limited by their individual spending limits of \$1000 per month. For more information, see the guide [Balance Floor and Ceiling and Linked Accounts](/v2/docs/balance-floor-ceiling).
Note that the only payment rail enabled for this account template is cards.
The request should return a new account template. We will refer to this template's UUID as `{{employee_account_template_id}}`.
With this template we can now create accounts for all the business's employees by performing a [POST /v0/accounts](/v2/reference/createaccount) with the following request body.
```json JSON theme={"system"}
{
"account_template_id": "{{employee_account_template_id}}",
"relationships": [
{
"relationship_type": "ACCOUNT_HOLDER",
"customer_id": "{{employee1_id}}"
}
]
}
```
This will create a new account. We will refer to the new account's ID as `{{employee1_account_id}}`. Repeat this request using `{{employee2_id}}` to create `{{employee2_account_id}}`, then using `{{employee3_id}}` to create `{{employee3_account_id}}`, etc.
## Employee cards
Create a card for each employee by performing a [POST /v0/cards](/v2/reference/issuecard) with the following request body. This assumes your card product is for physical cards. For virtual cards replace `"PHYSICAL"` with `"VIRTUAL"`.
```json JSON theme={"system"}
{
"form": "PHYSICAL",
"account_id": "{{employee1_account_id}}",
"card_product_id": "{{card_product_id}}",
"customer_id": "{{employee1_id}}",
"type": "DEBIT"
}
```
Then repeat this request to create cards for the other employees and their accounts.
## Transactions
Experiment with the employee cards by doing some simulated card transactions. Card transaction authorizations will automatically create "JIT funding" transactions between the business account and the respective employee accounts. For example, a $10 authorization for employee 1's card will decrease the balance of the business account by $10 and increase the ledger balance of employee 1's account by $10. This $10 will be placed on hold so the available balance of employee 1's account remains zero. If this authorization is reversed the \$10 automatically moves back into the business account. Card transactions will appear in the employee account transaction histories. There will be corresponding "JIT funding" transactions that appear in the employee accounts and the business accounts.
The monthly limit applies to each employee independently. Any attempt to authorize a transaction over the limit will be declined.
# Card Issuer 3DS
Source: https://docs.synctera.com/v2/docs/card-3ds
"Three Domain Secure" (3DS) is a protocol designed to be an additional security layer for credit and debit card transactions when there is no physical card present, such as in online purchases.
## Overview
When a merchant initiates the 3DS process the Synctera platform responds according to your configuration. You may specify the desired 3DS behaviour at the card product level or on a per-request basis.
### Card Product
Each product can be configured with a `three_ds_policy`. This field has two possible values: `SMS_OTP` and `EXEMPT`. If no value is specified, the default is `SMS_OTP`.
If you choose `SMS_OTP` then for each 3DS process involving the card product the cardholder will receive an SMS message with a one-time code which they must then supply to the merchant to verify their identity. The SMS will be sent to the customer's phone number that is stored in the platform.
If you choose `EXEMPT` then each 3DS process involving the card product will succeed with no further steps.
You may override the card product's 3DS policy by implementing a decision gateway.
### Decision Gateway
3DS decision gateways allow you to make real-time decision about each 3DS request. 3DS gateways are independent entities in the Synctera platform which can be associated with any number of card products. These associations are defined by the `three_ds_decision_gateways` endpoints, not the card product endpoints.
#### Creation
Create a 3DS decision gateway by calling `POST /v1/cards/three_ds_decision_gateways` with a body like this:
```json JSON theme={"system"}
{
"is_active": true,
"decision_url": "https://your.server/3ds_decision",
"card_products": [
"2957a146-ce3b-4d04-8328-ab3ea6e76cac"
],
"fallback_decision": "EXEMPT",
"custom_headers": {
"X-Custom-Data": "arbitrary value"
}
}
```
Every time the Synctera platform makes a 3DS decision for a card from the card products you specify, it will now attempt to send a `POST` request to your `decision_url`. The request will include all the `custom_headers` key-value pairs you specify as HTTP headers in the request.
Custom headers are optional to use as you see fit. For the purpose of authenticating requests, rather than using custom headers, you can [create a signature secret for request validation](/v2/docs/webhooks-guide#integration-steps) and then check the `Synctera-Signature` HTTP header of each request to your gateway.
The `fallback_decision` will be used if something goes wrong and the Synctera platform can't get a response from your decision endpoint or the request times out. The values for `fallback_decision` are the same as the `three_ds_policy` options for a card product (`SMS_OTP` and `EXEMPT`) and have the same meaning.
If an active decision gateway is configured for a card product, then the API responses for that card product will indicate that the `three_ds_policy` is `DECISION_GATEWAY`. This means that the decision gateway overrides the `three_ds_policy` of the card product and if the Synctera platform can't get a response from the decision gateway it will use the gateway's `fallback_decision` rather than the card product's `three_ds_policy`.
#### 3DS Decision Request
The body of the request to your 3DS decision server will look like this:
```json JSON theme={"system"}
{
"card_id": "f8c84f73-91a5-4dfe-9c12-a35bfa1df716",
"card_product_id": "2957a146-ce3b-4d04-8328-ab3ea6e76cac",
"acs_transaction_id": "a0c165f9-23ae-408c-b8c3-a7c486750b1e",
"authentication_request_type": "PAYMENT",
"client_ip_address": "10.1.2.3",
"device_channel": "BROWSER",
"transaction_amount": 6187,
"currency_code": "USD",
"transaction_type": "PAYMENT",
"transaction_sub_type": "PURCHASE",
"merchant": {
"name": "Best Buy",
"country_code": "840",
"id": "345954985882",
"category_code": "5732"
}
}
```
The `transaction_amount` is in minor units of the currency (in this case, cents). The currency is specified in the `currency_code` field as a 3 character alphabetical code.
`authentication_request_type` is one of `PAYMENT`, `RECURRING`, `INSTALLMENT`, `ADD_CARD`, `MAINTAIN_CARD` or `EMV_CARDHOLDER_VERIFICATION`.
`device_channel` is one of `BROWSER`, `APP_BASED` or `THREEDS_REQUESTER_INITIATED`.
`transaction_type` is either `PAYMENT` or `NON_PAYMENT`.
`transaction_sub_type` is one of `PURCHASE`, `ACCOUNT_VERIFICATION`, `ACCOUNT_FUNDING`, `QUASI_CASH` or `PREPAID_ACTIVATION_AND_LOAD`.
The `merchant.country_code` is specified as the ISO 3166-1 three-digit numeric country code.
#### 3DS Decision Response
To indicate your 3DS decision, your 3DS decision server responds with HTTP status OK (200) and writes a single field the response body. For example:
```json JSON theme={"system"}
{
"decision": "EXEMPT"
}
```
The allowed values for your `decision` are `SMS_OTP` and `EXEMPT`. These values have the same meaning as described above for the `three_ds_policy` of a card product.
If the Synctera platform sees a response status code other than 200 or can't decode the response body then it will use the gateway's `fallback_decision`.
# Card Activation Widget (Deprecated)
Source: https://docs.synctera.com/v2/docs/card-activation-widget-legacy
Using this widget you can add card activation to your website. It requires a Synctera widget vault token to initialize it with. The user will have 5 minutes to complete the activation submission.
This widget is deprecated. For new integrations, use the [Activate Card Widget](/v2/docs/card-widgets-activate).
The Card Activation Widget injects a configurable set of iframes into your app, allowing your user to securely enter a PAN & CVV in a PCI compliant manner. This helps remove some (but not all) of the PCI compliance requirements you would otherwise need to handle.
The iframes injected by Synctera allow you complete control over the styling over the widget using only CSS.
## Quick start
1. Add `` to your page.
2. Embed the Card Activation widgets when and as needed (both the card-pan and card-cvv widgets are needed):
```html HTML theme={"system"}
```
3. Add your own submit button under the Card Activation widgets.
4. Listen for the `validity` event on the Controller Card Activation widget (the one with the id):
```typescript TypeScript theme={"system"}
const activationWidget = document.getElementById('synctera-activate);
activationWidget.onValidity = (e, isValid) => button.disabled = !isValid;
```
5. Have the button call `activationWidget.submit()` to activate the card.It returns a promise, which resolves if the activation submission was successful, or rejects if it failed.
### *Basic Example*:
```html HTML theme={"system"}
PAN
CVV
```
**Styling: If you add a custom class name to the Card Activation widget, custom styling will be activated (see [Custom Styling](#custom-styling)).** By default, the widgets use the browser's inbuilt styling for the Card PAN and Card CVV input text fields (note that without custom styling the widget iframe is 4px bigger than the input field inside to allow for any browser outline effects).
*See [Custom Styling Example](#custom-styling-example) for a complete working example.*
## Environments
* Sandbox: [https://widgets-sandbox.synctera.com/assets/activate/activate\_v1.0.1.js](https://widgets-sandbox.synctera.com/assets/activate/activate_v1.0.1.js)
* Production: [https://widgets.synctera.com/assets/activate/activate\_v1.0.1.js](https://widgets.synctera.com/assets/activate/activate_v1.0.1.js)
## Browser support
The widgets work on both mobile and desktop, and we ensure support for all modern browsers. It also works in many older browsers, which we try to support where feasible.
| **Browser** | **Minimum tested version** |
| ----------- | -------------------------- |
| Chrome | 29 (2013) |
| Firefox | 27 (2014) |
| Edge | 79 (2020), 15 (2017) |
| Safari | 12 (2018) |
| Opera | 20 (2014) |
| IE | 11 (2013) |
## Card Activation flow
> **Summary**
>
> Fetch widget token from backend → Render widgets with token → Listen for "validity" event → User submits *controller* widget → Widget submits PAN & CVV to Synctera → And returns success or failure → Widget auto-destroys
**Note: It is recommended to also add 'load' and 'error' [listeners](#events) for UI management (see [Widget API](#widget-api) below).**
The flow for using the widgets:
1. On your backend, request a widget token from Synctera for the particular card you wish to use.
2. Make sure the Synctera Activation widget Javascript has been added to your website.
3. Render the two specialized Card PAN and Card CVV HTML tags as above, setting the token attribute, and your own button to submit the widget.
4. You can style these fields however you like (as explained below in [Custom Styling](#custom-styling)).
5. Listen for the "validity" event from the *controller* widget.
6. When valid, enable your button.
7. When the button is clicked, call the `submit()` function on the *controller* widget.
8. The submit function will activate the card.
9. It also returns a promise which resolves when successful, or rejects when there's a failure.
10. Once submitted, the widgets will auto-destroy and you can now remove them.
## Widget API
### Element Attributes:
| **Name** | **Value** | **Default** | **Details** | **Example** |
| --------- | ------------------- | ----------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------- |
| **token** | widget token ID | - | Required | `` |
| **class** | class name (string) | Omitted | Optional*If present, it will activate custom styling* | ``\\` |
### Events:
| **Name** | **Called when...** | **Why?** | ***addEventListener*** | ***on*** |
| ------------ | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| **load** | The widgets have are fully loaded | For best user experience, show the widgets only when this event is triggered | `cardPanEl.addEventListener('load', () => ...);` | `cardPanEl.onLoad = () => ...` |
| **validity** | The widgets are now (in)valid | Only allow `submit()` to be called when valid*To get the status, check the "isValid" property* | `cardPanEl.addEventListener('validity', () => {``␠ buttonEl.disabled = !cardPanEl.isValid;``});` | `cardPanEl.onValidity = (e, isValid) => {``␠ buttonEl.disabled = !isValid;``};` |
| **error** | The widgets failed to load | Non-recoverable error states*To get the error, check the "error" property* | `cardPanEl.addEventListener('error', () => {``␠ console.log(cardPanEl.error);``});` | `cardPanEl.onError = () => {``␠ console.log(cardPanEl.error);``};` |
| **success** | The widget submitted successfully | Know when the widget is done and the card was activated*Alternatively: The "submit()" method returns a promise resolving when done* | `cardPanEl.addEventListener('success', () => ...);` | `cardPanEl.onSuccess = () => ...` |
| **failure** | The widget submitted unsuccessfully | Know if the card was not activated and the user will need to try again*Alternatively: The "submit()" method returns a promise rejecting on failure\*\*To get the failure, check the "error" property* | `cardPanEl.addEventListener('failure', () => {``␠ console.log(cardPanEl.error);``});` | `cardPanEl.onFailure = (e, errorDetails) => ...` |
### Fields:
| **Name** | **Value / Parameters** | **Default / Returns** | **Details** | **Example** |
| ------------ | ------------------------------ | --------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| **isValid** | truefalse | false | true if both fields are valid and form is ready to submit | `cardPanEl.isValid` |
| **error** | \{ errorType:'...', error: \*} | undefined | Error details if an error or failure occurs | `cardPanEl.error` |
| **submit()** | *No parameters* | *Returns:* Promise | Submits the PAN & CVV to Synctera's servers*Alternatively:* The widget fires "success" and "failure" events | `cardPanEl.submit()``␠ .then(// success)``␠ .catch(// error)` |
## Custom styling
> **Summary**
>
> * Add a class name to `card-pan` or `card-cvv` tags to activate Custom Styling: ` * Style as desired (i.e. border), but [Font Styles](#font-styles) are special
> * For pseudo-selectors, e.g. `:hover`, `:focus`, etc - use pseudo-attributes instead: *Example:* `.customStyles[_hover] { border: 1px solid #616161; }`
> * Font styles are *auto-forwarded*, but only certain values are allowed (see below)
> * Optional: You can use `.customStyles[_required]` to show the widgets once they have loaded
By default, without custom styling, the widgets use the browser's inbuilt styling for the PAN and CVV input text fields (note that the widget iframe is 4px bigger than the input field inside to allow for any browser outline effects).
You may want to style the widget to match your webapp design or to add things like placeholders. This can be done entirely through css styling. When a `class` attribute is added to the widget, the 4px spacing is removed, the input box styling is stripped and the background is made transparent such that any elements placed behind the widget will be visible (i.e. a placeholder element). The text input is set to always take up 100% of the height and width of the iframe, and likewise for the iframe in the widget tag.
Input text field pseudo selectors such as `:hover`, `:focus` and `:blank` are indirectly supported through css-like attributes instead of actual pseudo selectors. As the user hovers, focuses and types in the field, attributes will be added/removed on the `card-pan` and `card-cvv` tags automatically. Supported "pseudo attributes" are inspired from: [https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes#input\_pseudo-classes](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes#input_pseudo-classes)
*Where `customStyles` is the class name added to widget tags, e.g. `
```html html theme={"system"}
PAN
CVV
```
## Font Styles
You can style the or tag as you see fit (border, background, etc). Css font properties are handled in a special way to forward them to the iframe for styling the input, in a secure and sanitized manner. As such only certain css font properties are supported and only with certain values (all other values may be ignored, if they work they are not guaranteed across all browsers):
| **Font css property name** | **Allowed values** |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| color | `rgb(, , )` or `rgba(, , , )` |
| font-family | **One of:** courier: `Courier New, Courier, Lucida Console, Lucida Sans Typewriter, monospace` arial: `Arial, Helvetica, sans-serif` georgia: `Georgia, Times New Roman, Times, serif` helvetica: `Helvetica, Arial, sans-serif` lucida: `Lucida Console, Lucida Sans Typewriter, Courier New, Courier, monospace` times: `Times New Roman, Times, Georgia, serif` tahoma: `Tahoma, Verdana, sans-serif` verdana: `Verdana, Tahoma, sans-serif` |
| font-size | `px` |
| font-weight | `00` (a number, not 'bold' etc) |
| line-height | `px` |
| letter-spacing | `px` |
Note that with the `font-family` property, we look for a keyword in the first font in the comma separated list of fonts you supply. If there's a match, we use the font-family strings in the above table and not the font-family specified (for security). The above list of fonts are designed to ensure maximum coverage across different operating systems for similar style fonts. Custom fonts are not supported at this time.
## Additional Resources
For examples showing the Card Activation & Set PIN widget implementation please see the following:
# Card Transaction Disputes
Source: https://docs.synctera.com/v2/docs/card-transaction-disputes
Manage card disputes with the Synctera API: submit disputes, upload supporting documentation, and track status through the card network.
## Version 2 Rollout
This is the **Version 2 (V2)** Card Transaction Disputes guide. For the Version 1 flow, see [Card Transaction Disputes (V1)](/v1/docs/card-transaction-disputes).
The planned rollout for Version 2 of Synctera's Dispute Management is as follows:
* **Mastercard programs:** September 7 – October 7, 2026
* Synctera will notify you of the exact date. Until then, follow the [V1](/v1/docs/card-transaction-disputes) flow.
* **Visa and PULSE programs:** TBD
* Keep following the [V1](/v1/docs/card-transaction-disputes) flow until further notice.
**Key changes between V1 and V2:**
* Reason codes have been streamlined (deprecated codes remain accepted for backward compatibility)
* Comprehensive evidence gathering at dispute creation to strengthen the case — see [`evidence`](/v2/reference/createdispute#body-one-of-0-evidence) in the Create Dispute spec.
* New lifecycle states, including more granular declined-dispute states: Withdrawn, Refunded, Expired, Rejected
* Dispute document categories — some reason codes require specific categories on submission
## Overview
In order to manage the card dispute process, the Synctera API provides support for the required tasks such as submitting a dispute, uploading supporting documentation, and tracking status through the card network. Synctera evaluates evidence and interacts with the card network on your behalf. As mentioned under [Transaction Disputes](/v2/docs/transaction-disputes), a [Dispute Case](/v2/docs/dispute-cases-fintechs) gets automatically created once a dispute is opened. The Dispute Case is used to track and reflect the dispute status through the Synctera Console.
## What Transactions Can Be Disputed?
* Only posted transactions can be disputed, and only if within 120 days from the settlement. A pending transaction cannot be disputed.
* A dispute cannot exceed the amount of the original transaction.
* For fraud disputes (`reason_code` = `UNAUTHORIZED_TRANSACTION`), the card must be terminated before the dispute can be created.
## Chargeback Dispute Lifecycle
The chargeback dispute lifecycle involves a series of **actions** between the issuer and acquirer, which continues until a decision is reached on who is financially responsible for the dispute. The dispute lifecycle is also described in [this article](/v2/docs/dispute-cases-fintechs).
Card disputes start in `EVIDENCE_GATHERING`. After evidence is submitted via `EVIDENCE_GATHERING.COMPLETE`, Synctera evaluates the case and files with the card network when appropriate. Subsequent lifecycle updates (chargeback, representment, outcomes, and similar) are applied automatically and surfaced via `DISPUTE.UPDATED` webhooks and GET responses.
#### 1. Chargeback
> Time frame within 120 days of settlement of the transaction.
A chargeback is filed when the transaction meets the conditions listed in the [reason codes](/v2/docs/card-transaction-disputes#reason-code) found below. This happens after `EVIDENCE_GATHERING.COMPLETE`, when Synctera determines a chargeback should be filed.
#### 2. Representment
> Time frame within 45 days of chargeback.
A representment occurs when the acquiring bank either has evidence to prove that the chargeback does not meet the requirements of the reason code or can provide information that addresses the original reason for the dispute.
#### 3. Pre-arbitration
> Time frame within 45 days of representment.
Pre-arbitration is the final chance for the issuing bank to provide further evidence that the cardholder should be refunded for their transaction.
#### 4. Arbitration
> Time frame within 75 days of pre-arbitration.
In the event that the issuer and acquirer cannot settle on an outcome of the financial responsibility of the transaction, the dispute can be raised to arbitration. Arbitration leaves the decision of who is financially responsible in the hands of third party arbitrators (the card network).
Issuers and acquirers generally choose to avoid arbitration as it involves fees as high as \$500 or more per case in addition to any other fees associated with the dispute.
## Write-Offs
In cases where a transaction's value is lower than the cost of pursuing a dispute, it can be written off instead of filing with the card network. A write-off is submitted (`WRITE_OFF.SUBMITTED`) automatically or by an issuer, then accepted automatically (`WRITE_OFF.ACCEPTED`). Any provisional credit is reversed, a final credit is posted, and the dispute is closed.
### Automatic Write-Offs
Synctera can configure an automatic write-off threshold for your program. When a dispute is created and both of the following are true, Synctera routes it through the automatic write-off workflow instead of filing with the network:
* The disputed amount is less than or equal to the configured threshold
* The dispute is created no more than 120 days after the disputed transaction's effective date
Those disputes are created with `managed_by` = `AUTO_WRITE_OFF` and `network` = `NONE`. Synctera later submits a `WRITE_OFF` action automatically, moving the dispute to the `WRITE_OFF` lifecycle. No issuer network actions are available on automatic write-off disputes. Automatic write-off is enabled by default with a **\$25** threshold. Contact your Synctera representative to disable it or change the threshold for your program.
## Reason Code
When creating a dispute, the **Reason Code** is the most critical field in your request. It acts as the formal classification for the claim, signaling to the network exactly why the transaction is being challenged. Selecting the correct code ensures the case is evaluated under the proper network rules and significantly improves the chances of a successful resolution.
#### Selecting a Reason Code
To ensure your dispute meets all technical requirements, please refer to the supported codes in our documentation.
**View the Codes:** Click the link below and ensure the **Card Dispute** tab is selected in the **Body** parameters section to see the full list of codes and required documentation.
* [View Available Reason Codes](/v2/reference/createdispute)
Some reason codes also require additional `evidence` on create, or a dispute document uploaded with a specific `category` before the case can be filed — see [Upload Supporting Documents](/v2/docs/transaction-disputes#2-upload-supporting-documents).
Please ensure you are using **Active** codes. While some legacy codes are still accepted for backward compatibility, they are deprecated and will be automatically mapped to their modern equivalents by the API. Using the active code from the start ensures better tracking and transparency for your case.
## Card Dispute Lifecycle States
| Lifecycle | Description |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| EVIDENCE\_GATHERING | Initial state after a dispute is created. Supporting documents and evidence can be added before the case is filed with the network. |
| EVALUATION | Evidence gathering is complete and the case has been submitted for evaluation / network filing. |
| MANUAL\_REVIEW | The case requires manual review before proceeding. |
| WRITE\_OFF | The dispute has been written off and will not be filed with the card network. |
| CHARGEBACK | A chargeback has been filed with the card network. The acquiring bank may accept or file a re-presentment. |
| REPRESENTMENT | The acquiring bank has filed a re-presentment. The case may proceed to pre-arbitration or resolve as won/lost. |
| PRE\_ARBITRATION | A pre-arbitration case has been created with the network. The acquiring bank may rebut or the case may resolve. |
| PRE\_ARBITRATION\_RESPONSE | The acquiring bank has rebutted the pre-arbitration case. The case may escalate to arbitration or resolve. |
| ARBITRATION | An arbitration case has been created with the card network. The network determines the outcome. |
| WITHDRAWN | The dispute has been withdrawn at the request of the cardholder. |
| REFUNDED | The cardholder has been refunded directly by the merchant through a separate channel. |
| EXPIRED | The dispute has expired. |
| REJECTED | The dispute has been rejected. |
## Available Actions
Actions on a card dispute come from different actors. FinTech actions are available via the Disputes API. Issuer actions may be issued automatically or manually by a Synctera operator when a case needs review. Acquirer / network actions are applied automatically as the case progresses with the card network and appear in `action_history` via `DISPUTE.UPDATED` webhooks.
### Issuer Actions (FinTech)
Issuer actions available to FinTechs via the Disputes API.
| Lifecycle | Action | Decision | Description |
| ------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| EVIDENCE\_GATHERING | `EVIDENCE_GATHERING.COMPLETE` | ONGOING | Submit collected evidence and file the case with the network. Moves the dispute to `EVALUATION`. |
| \* (any open state) | `PROVISIONAL_CREDIT.CREATE` | — | Issue provisional credit as required by regulation or policy. |
| \* (any open state) | `PROVISIONAL_CREDIT.REVERSE` | — | Reverse provisional credit as required by regulation or policy. |
### Issuer Actions (Synctera)
Issuer actions that may be issued automatically or manually by a Synctera operator (for example in `MANUAL_REVIEW` after chargeback or representment review, or after a pre-arbitration rebuttal). These appear in `action_history` and trigger `DISPUTE.UPDATED` webhooks.
| Lifecycle | Action | Decision | Description |
| --------------------------- | ------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------- |
| \* (any open state) | `PROVISIONAL_CREDIT.SUBMITTED` | — | Issue provisional credit as required by regulation or policy. |
| \* (any open state) | `PROVISIONAL_CREDIT.REVERSED` | — | Reverse provisional credit as required by regulation or policy. |
| EVALUATION / MANUAL\_REVIEW | `WRITE_OFF.SUBMITTED` | ONGOING | Case written off without (or instead of) pursuing a chargeback. Moves to `WRITE_OFF`. |
| EVALUATION / MANUAL\_REVIEW | `CHARGEBACK.SUBMITTED` | ONGOING | Chargeback filed with the card network. Moves to `CHARGEBACK`. |
| EVALUATION / MANUAL\_REVIEW | `DISPUTE.REJECTED` | NONE | Case rejected. |
| CHARGEBACK | `DISPUTE.WITHDRAWN` | NONE | Case withdrawn while in chargeback. |
| REPRESENTMENT | `WRITE_OFF.SUBMITTED` | ONGOING | Case written off instead of continuing after representment; issuer accepts liability. Moves to `WRITE_OFF`. |
| REPRESENTMENT | `DISPUTE.LOST` | LOST | Cardholder accepts liability after representment; case lost. |
| REPRESENTMENT | `DISPUTE.REFUNDED` | NONE | Merchant credited the cardholder. |
| REPRESENTMENT | `DISPUTE.WITHDRAWN` | NONE | Case withdrawn while in representment. |
| MANUAL\_REVIEW | `PRE_ARBITRATION.SUBMITTED` | ONGOING | Pre-arbitration filed after representment review. Moves to `PRE_ARBITRATION`. |
| MANUAL\_REVIEW | `DISPUTE.WITHDRAWN` | NONE | Case withdrawn at cardholder / Synctera request. |
| PRE\_ARBITRATION\_RESPONSE | `WRITE_OFF.SUBMITTED` | ONGOING | Case written off instead of pursuing arbitration; issuer accepts liability. Moves to `WRITE_OFF`. |
| PRE\_ARBITRATION\_RESPONSE | `DISPUTE.LOST` | LOST | Cardholder accepts liability after pre-arbitration rebuttal; case lost. |
| PRE\_ARBITRATION\_RESPONSE | `DISPUTE.REFUNDED` | NONE | Merchant credited the cardholder. |
| PRE\_ARBITRATION\_RESPONSE | `ARBITRATION.SUBMITTED` | ONGOING | Arbitration filed after pre-arbitration rebuttal. Moves to `ARBITRATION`. |
| PRE\_ARBITRATION\_RESPONSE | `DISPUTE.WITHDRAWN` | NONE | Case withdrawn after pre-arbitration response. |
### Acquirer / Network Actions
Applied automatically as the dispute progresses with the acquirer and card network. Action values below match `action_history` responses.
| Lifecycle | Action | Decision | Description |
| --------------------------- | --------------------------- | -------- | ---------------------------------------------------------------------------------- |
| EVALUATION / MANUAL\_REVIEW | `DISPUTE.REFUNDED` | NONE | Merchant credited the cardholder outside the chargeback flow. Moves to `REFUNDED`. |
| EVALUATION / MANUAL\_REVIEW | `DISPUTE.EXPIRED` | NONE | Case expired. |
| CHARGEBACK | `REPRESENTMENT.SUBMITTED` | ONGOING | Acquirer filed a re-presentment. Moves to `REPRESENTMENT`. |
| CHARGEBACK | `DISPUTE.WON` | WON | Chargeback accepted / case won (no representment). |
| REPRESENTMENT | `PRE_ARBITRATION.SUBMITTED` | ONGOING | Pre-arbitration filed. Moves to `PRE_ARBITRATION`. |
| PRE\_ARBITRATION | `PRE_ARBITRATION.REBUTTED` | ONGOING | Acquirer rebutted pre-arbitration. Moves to `PRE_ARBITRATION_RESPONSE`. |
| PRE\_ARBITRATION | `DISPUTE.WON` | WON | Case won in pre-arbitration. |
| PRE\_ARBITRATION | `DISPUTE.LOST` | LOST | Case lost in pre-arbitration. |
| ARBITRATION | `DISPUTE.WON` | WON | Case won in arbitration. |
| ARBITRATION | `DISPUTE.LOST` | LOST | Case lost in arbitration. |
## Provisional Credits
Provisional credits may be issued to the cardholder as a temporary measure while investigating the dispute. Whether or not a provisional credit is required, is determined by the applicable regulations.
The relevant regulations/timelines that apply can be derived from the transaction being disputed by identifying which type of card program the transaction belongs to.
| BIN Country | Customer Type | Card/BIN Type | Regulation |
| ----------- | ------------- | ------------- | ------------ |
| US | Consumer | Debit | Regulation E |
| US | Consumer | Credit | Regulation Z |
| US | Commercial | Debit | N/A |
| US | Commercial | Credit | N/A |
### To Summarize:
* Regulation E (US) applies to consumer debit transactions - it mandates issuance of provisional credit to the customer account while the dispute is under investigation (within 10 business days for established customers, and within 20 business days for new customers)
* Regulation Z (US) applies to consumer credit transactions - it mandates that a transaction that is under dispute is not included in the outstanding/due balances, and is not included in the available credit balance
* These regulations also have strict timelines around customer notifications, which is ultimately the responsibility of the FinTech - for details, see [this article](/v2/docs/customer-support-and-complaints)
* There are no specific regulations around commercial transactions
* Card networks also have their own “zero liability” policies - cardholders won’t be held responsible for unauthorized charges made with their card or card information provided they promptly report the issue
* Note that although issuance of provisional credit may not be mandated by regulations, the FinTech may still decide to issue provisional credit
## Complex Business Flow Example
The following example walks through a card dispute: create → upload evidence → complete evidence gathering → chargeback filed → provisional credit → representment → dispute lost.
### 1. Dispute Created
A dispute is created on behalf of the cardholder. Disputes start in `EVIDENCE_GATHERING`. Include any required `evidence` fields for the chosen `reason_code`.
##### Request
```bash Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/disputes \
-H "Authorization: Bearer $apiKey" \
--json '
{
"payment_rail": "CARD",
"transaction_id": "{$transaction_id}",
"disputed_amount": 500,
"date_customer_reported": "2024-05-28T12:25:00.000Z",
"memo": "Ordered item never arrived.",
"reason_code": "GOODS_OR_SERVICES_NOT_PROVIDED",
"evidence": {
"merchant_contact": {
"was_attempted": true,
"date": "2024-05-20",
"was_successful": false,
"description": "Called merchant support; no tracking information provided."
},
"delivery": {
"expected_date": "2024-05-15"
}
}
}
'
```
##### Response
```json JSON theme={"system"}
{
"account_id": "018fc166-8874-7f3c-943a-178ad1c31903",
"applicable_regulation": "REGULATION_Z",
"creation_time": "2024-05-28T22:48:24.279984Z",
"credit_status": "NONE",
"currency": "USD",
"customer_id": "018fc166-b34b-7211-aa8d-1ca6c6b7b1e8",
"date_customer_reported": "2024-05-28T12:25:00Z",
"decision": "ONGOING",
"dispute_documents": [],
"disputed_amount": 500,
"id": "018fc167-7672-729d-8d43-653518f3d939",
"last_updated_time": "2024-05-28T22:48:24.279984Z",
"managed_by": "GROUND_CONTROL",
"memo": "Ordered item never arrived.",
"network": "DECISIONLY",
"payment_rail": "CARD",
"status": "OPEN",
"tenant": "asbght_iujkio",
"transaction_id": "018fc168-3ce3-7839-8cd3-a653bc4aa9bc",
"action_history": [],
"available_actions": [
{
"action": "PROVISIONAL_CREDIT",
"state": "CREATE"
},
{
"action": "EVIDENCE_GATHERING",
"state": "COMPLETE"
}
],
"lifecycle_state": "EVIDENCE_GATHERING",
"network_eligibility": {
"is_digital_wallet_token": false,
"is_three_ds": false,
"is_network_eligibility_overridden": false
},
"reason_code": "GOODS_OR_SERVICES_NOT_PROVIDED",
"evidence": {
"merchant_contact": {
"was_attempted": true,
"date": "2024-05-20",
"was_successful": false,
"description": "Called merchant support; no tracking information provided."
},
"delivery": {
"expected_date": "2024-05-15"
}
},
"acquirer_reference_number": "52405245204967729855476"
}
```
### 2. Upload Documents and Complete Evidence Gathering
Supporting documentation is uploaded to the dispute. Files must be JPEG, PNG, or PDF, max 4.5MB each, up to 10 documents. Optionally set `category` (for example `TRANSACTION_RECEIPT`, `MERCHANT_CORRESPONDENCE`, `OTHER`).
##### Request
```bash Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/disputes/{$dispute_id}/documents \
-H "Authorization: Bearer $apiKey" \
-F file=@file.pdf \
-F category=OTHER
```
##### Response
```json JSON theme={"system"}
{
"creation_time": "2024-05-28T22:56:12.921781Z",
"dispute_id": "018fc167-7672-729d-8d43-653518f3d939",
"file_name": "file.pdf",
"id": "018fc16b-bd41-70bc-89fe-f4330867ba73",
"category": "OTHER",
"tenant": "asbght_iujkio"
}
```
Complete evidence gathering to create and file the case with the network.
##### Request
```bash Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/disputes/{$dispute_id}/actions \
-H "Authorization: Bearer $apiKey" \
--json '
{
"payment_rail": "CARD",
"action": "EVIDENCE_GATHERING",
"state": "COMPLETE"
}
'
```
##### Response
```json JSON theme={"system"}
{
"id": "018fc16e-0355-7cea-bf80-b0eddd63d48a",
"creation_time": "2024-05-28T22:58:30.396998Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "EVIDENCE_GATHERING",
"status": "COMPLETED",
"external_reference_id": "018fc167-7672-729d-8d43-653518f3d939"
}
```
#### Dispute State
The dispute moves to `EVALUATION`. Synctera then evaluates and may file a chargeback (or write-off / request review). Further network steps arrive as webhook-driven action history updates.
```json JSON theme={"system"}
{
"account_id": "018fc166-8874-7f3c-943a-178ad1c31903",
"applicable_regulation": "REGULATION_Z",
"creation_time": "2024-05-28T22:48:24.279984Z",
"credit_status": "NONE",
"currency": "USD",
"customer_id": "018fc166-b34b-7211-aa8d-1ca6c6b7b1e8",
"date_customer_reported": "2024-05-28T12:25:00Z",
"decision": "ONGOING",
"dispute_documents": [
{
"creation_time": "2024-05-28T22:56:12.921781Z",
"dispute_id": "018fc167-7672-729d-8d43-653518f3d939",
"file_name": "file.pdf",
"id": "018fc16b-bd41-70bc-89fe-f4330867ba73",
"category": "OTHER",
"tenant": "asbght_iujkio"
}
],
"disputed_amount": 500,
"id": "018fc167-7672-729d-8d43-653518f3d939",
"last_action_by": "INITIATOR",
"last_updated_time": "2024-05-28T22:58:30.396998Z",
"managed_by": "GROUND_CONTROL",
"memo": "Ordered item never arrived.",
"network": "DECISIONLY",
"payment_rail": "CARD",
"status": "OPEN",
"tenant": "asbght_iujkio",
"transaction_id": "018fc168-3ce3-7839-8cd3-a653bc4aa9bc",
"action_history": [
{
"id": "018fc16e-0355-7cea-bf80-b0eddd63d48a",
"creation_time": "2024-05-28T22:58:30.396998Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "EVIDENCE_GATHERING",
"status": "COMPLETED",
"external_reference_id": "018fc167-7672-729d-8d43-653518f3d939"
}
],
"available_actions": [
{
"action": "PROVISIONAL_CREDIT",
"state": "CREATE"
}
],
"lifecycle_state": "EVALUATION",
"network_eligibility": {
"is_digital_wallet_token": false,
"is_three_ds": false,
"is_network_eligibility_overridden": false
},
"reason_code": "GOODS_OR_SERVICES_NOT_PROVIDED",
"evidence": {
"merchant_contact": {
"was_attempted": true,
"date": "2024-05-20",
"was_successful": false,
"description": "Called merchant support; no tracking information provided."
},
"delivery": {
"expected_date": "2024-05-15"
}
},
"acquirer_reference_number": "52405245204967729855476"
}
```
### 3. Chargeback Submitted
After evaluation, Synctera files a chargeback with the card network. This appears as a `DISPUTE.UPDATED` webhook with `CHARGEBACK.SUBMITTED` in `action_history`. The dispute moves to `CHARGEBACK`.
#### Dispute State
```json JSON theme={"system"}
{
"account_id": "018fc166-8874-7f3c-943a-178ad1c31903",
"applicable_regulation": "REGULATION_Z",
"creation_time": "2024-05-28T22:48:24.279984Z",
"credit_status": "NONE",
"currency": "USD",
"customer_id": "018fc166-b34b-7211-aa8d-1ca6c6b7b1e8",
"date_customer_reported": "2024-05-28T12:25:00Z",
"decision": "ONGOING",
"dispute_documents": [
{
"creation_time": "2024-05-28T22:56:12.921781Z",
"dispute_id": "018fc167-7672-729d-8d43-653518f3d939",
"file_name": "file.pdf",
"id": "018fc16b-bd41-70bc-89fe-f4330867ba73",
"category": "OTHER",
"tenant": "asbght_iujkio"
}
],
"disputed_amount": 500,
"id": "018fc167-7672-729d-8d43-653518f3d939",
"last_action_by": "NETWORK",
"last_updated_time": "2024-05-29T09:00:00.000000Z",
"managed_by": "GROUND_CONTROL",
"memo": "Ordered item never arrived.",
"network": "DECISIONLY",
"payment_rail": "CARD",
"status": "OPEN",
"tenant": "asbght_iujkio",
"transaction_id": "018fc168-3ce3-7839-8cd3-a653bc4aa9bc",
"action_history": [
{
"id": "018fc16e-0355-7cea-bf80-b0eddd63d48a",
"creation_time": "2024-05-28T22:58:30.396998Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "EVIDENCE_GATHERING",
"status": "COMPLETED",
"external_reference_id": "018fc167-7672-729d-8d43-653518f3d939"
},
{
"id": "018fcb2e-1111-7cea-bf80-b0eddd63d48a",
"creation_time": "2024-05-29T09:00:00.000000Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "CHARGEBACK",
"status": "SUBMITTED"
}
],
"available_actions": [
{
"action": "PROVISIONAL_CREDIT",
"state": "CREATE"
}
],
"lifecycle_state": "CHARGEBACK",
"network_eligibility": {
"is_digital_wallet_token": false,
"is_three_ds": false,
"is_network_eligibility_overridden": false
},
"reason_code": "GOODS_OR_SERVICES_NOT_PROVIDED",
"evidence": {
"merchant_contact": {
"was_attempted": true,
"date": "2024-05-20",
"was_successful": false,
"description": "Called merchant support; no tracking information provided."
},
"delivery": {
"expected_date": "2024-05-15"
}
},
"acquirer_reference_number": "52405245204967729855476"
}
```
### 4. Post Provisional Credit
A provisional credit may be issued after the chargeback has been filed.
##### Request
```bash Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/disputes/{$dispute_id}/actions \
-H "Authorization: Bearer $apiKey" \
--json '
{
"payment_rail": "CARD",
"action": "PROVISIONAL_CREDIT",
"state": "CREATE"
}
'
```
##### Response
```json JSON theme={"system"}
{
"id": "018fcb2f-50f4-7514-a82d-e6143f57f25f",
"creation_time": "2024-05-29T10:00:00.000000Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "PROVISIONAL_CREDIT",
"status": "SUBMITTED"
}
```
#### Dispute State
```json JSON theme={"system"}
{
"account_id": "018fc166-8874-7f3c-943a-178ad1c31903",
"applicable_regulation": "REGULATION_Z",
"creation_time": "2024-05-28T22:48:24.279984Z",
"credit_status": "PROVISIONAL",
"currency": "USD",
"customer_id": "018fc166-b34b-7211-aa8d-1ca6c6b7b1e8",
"date_customer_reported": "2024-05-28T12:25:00Z",
"decision": "ONGOING",
"dispute_documents": [
{
"creation_time": "2024-05-28T22:56:12.921781Z",
"dispute_id": "018fc167-7672-729d-8d43-653518f3d939",
"file_name": "file.pdf",
"id": "018fc16b-bd41-70bc-89fe-f4330867ba73",
"category": "OTHER",
"tenant": "asbght_iujkio"
}
],
"disputed_amount": 500,
"id": "018fc167-7672-729d-8d43-653518f3d939",
"last_action_by": "INITIATOR",
"last_updated_time": "2024-05-29T10:00:00.000000Z",
"managed_by": "GROUND_CONTROL",
"memo": "Ordered item never arrived.",
"network": "DECISIONLY",
"payment_rail": "CARD",
"status": "OPEN",
"tenant": "asbght_iujkio",
"transaction_id": "018fc168-3ce3-7839-8cd3-a653bc4aa9bc",
"action_history": [
{
"id": "018fc16e-0355-7cea-bf80-b0eddd63d48a",
"creation_time": "2024-05-28T22:58:30.396998Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "EVIDENCE_GATHERING",
"status": "COMPLETED",
"external_reference_id": "018fc167-7672-729d-8d43-653518f3d939"
},
{
"id": "018fcb2e-1111-7cea-bf80-b0eddd63d48a",
"creation_time": "2024-05-29T09:00:00.000000Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "CHARGEBACK",
"status": "SUBMITTED"
},
{
"id": "018fcb2f-50f4-7514-a82d-e6143f57f25f",
"creation_time": "2024-05-29T10:00:00.000000Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "PROVISIONAL_CREDIT",
"status": "SUBMITTED"
}
],
"available_actions": [
{
"action": "PROVISIONAL_CREDIT",
"state": "REVERSE"
}
],
"lifecycle_state": "CHARGEBACK",
"network_eligibility": {
"is_digital_wallet_token": false,
"is_three_ds": false,
"is_network_eligibility_overridden": false
},
"reason_code": "GOODS_OR_SERVICES_NOT_PROVIDED",
"evidence": {
"merchant_contact": {
"was_attempted": true,
"date": "2024-05-20",
"was_successful": false,
"description": "Called merchant support; no tracking information provided."
},
"delivery": {
"expected_date": "2024-05-15"
}
},
"acquirer_reference_number": "52405245204967729855476"
}
```
### 5. Webhook Received for Representment
#### Dispute State
A representment from the acquirer is reflected on the dispute via `DISPUTE.UPDATED`, including any supporting documentation from the network.
```json JSON theme={"system"}
{
"account_id": "018fc166-8874-7f3c-943a-178ad1c31903",
"applicable_regulation": "REGULATION_Z",
"creation_time": "2024-05-28T22:48:24.279984Z",
"credit_status": "PROVISIONAL",
"currency": "USD",
"customer_id": "018fc166-b34b-7211-aa8d-1ca6c6b7b1e8",
"date_customer_reported": "2024-05-28T12:25:00Z",
"decision": "ONGOING",
"dispute_documents": [
{
"creation_time": "2024-05-28T22:56:12.921781Z",
"dispute_id": "018fc167-7672-729d-8d43-653518f3d939",
"file_name": "file.pdf",
"id": "018fc16b-bd41-70bc-89fe-f4330867ba73",
"category": "OTHER",
"tenant": "asbght_iujkio"
},
{
"creation_time": "2024-05-30T20:32:00.000000Z",
"dispute_id": "018fc167-7672-729d-8d43-653518f3d939",
"file_name": "representment.pdf",
"id": "018fcb30-90a1-7221-9160-28917c2cfc2d",
"tenant": "asbght_iujkio"
}
],
"disputed_amount": 500,
"id": "018fc167-7672-729d-8d43-653518f3d939",
"last_action_by": "NETWORK",
"last_updated_time": "2024-05-30T20:32:03.787519Z",
"managed_by": "GROUND_CONTROL",
"memo": "Ordered item never arrived.",
"network": "DECISIONLY",
"payment_rail": "CARD",
"status": "OPEN",
"tenant": "asbght_iujkio",
"transaction_id": "018fc168-3ce3-7839-8cd3-a653bc4aa9bc",
"action_history": [
{
"id": "018fc16e-0355-7cea-bf80-b0eddd63d48a",
"creation_time": "2024-05-28T22:58:30.396998Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "EVIDENCE_GATHERING",
"status": "COMPLETED",
"external_reference_id": "018fc167-7672-729d-8d43-653518f3d939"
},
{
"id": "018fcb2e-1111-7cea-bf80-b0eddd63d48a",
"creation_time": "2024-05-29T09:00:00.000000Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "CHARGEBACK",
"status": "SUBMITTED"
},
{
"id": "018fcb2f-50f4-7514-a82d-e6143f57f25f",
"creation_time": "2024-05-29T10:00:00.000000Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "PROVISIONAL_CREDIT",
"status": "SUBMITTED"
},
{
"id": "018e62f5-8141-708f-a924-c706d82876a5",
"creation_time": "2024-05-30T20:32:03.787519Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "REPRESENTMENT",
"status": "SUBMITTED",
"supporting_doc_id": "018fcb30-90a1-7221-9160-28917c2cfc2d",
"message": "Merchant provided proof of delivery"
}
],
"available_actions": [
{
"action": "PROVISIONAL_CREDIT",
"state": "REVERSE"
}
],
"lifecycle_state": "REPRESENTMENT",
"network_eligibility": {
"is_digital_wallet_token": false,
"is_three_ds": false,
"is_network_eligibility_overridden": false
},
"reason_code": "GOODS_OR_SERVICES_NOT_PROVIDED",
"evidence": {
"merchant_contact": {
"was_attempted": true,
"date": "2024-05-20",
"was_successful": false,
"description": "Called merchant support; no tracking information provided."
},
"delivery": {
"expected_date": "2024-05-15"
}
},
"acquirer_reference_number": "52405245204967729855476"
}
```
### 6. Dispute Lost
The issuer records that the cardholder accepts liability after representment (`DISPUTE.LOST`).
##### Request
```bash Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/disputes/{$dispute_id}/actions \
-H "Authorization: Bearer $apiKey" \
--json '
{
"payment_rail": "CARD",
"action": "DISPUTE",
"state": "LOST"
}
'
```
#### Dispute State
The dispute now reflects the final decision. Any outstanding provisional credit is reversed on a delay (not immediately).
```json JSON theme={"system"}
{
"account_id": "018fc166-8874-7f3c-943a-178ad1c31903",
"applicable_regulation": "REGULATION_Z",
"creation_time": "2024-05-28T22:48:24.279984Z",
"credit_status": "PROVISIONAL",
"currency": "USD",
"customer_id": "018fc166-b34b-7211-aa8d-1ca6c6b7b1e8",
"date_customer_reported": "2024-05-28T12:25:00Z",
"decision": "LOST",
"dispute_documents": [
{
"creation_time": "2024-05-28T22:56:12.921781Z",
"dispute_id": "018fc167-7672-729d-8d43-653518f3d939",
"file_name": "file.pdf",
"id": "018fc16b-bd41-70bc-89fe-f4330867ba73",
"category": "OTHER",
"tenant": "asbght_iujkio"
},
{
"creation_time": "2024-05-30T20:32:00.000000Z",
"dispute_id": "018fc167-7672-729d-8d43-653518f3d939",
"file_name": "representment.pdf",
"id": "018fcb30-90a1-7221-9160-28917c2cfc2d",
"tenant": "asbght_iujkio"
}
],
"disputed_amount": 500,
"id": "018fc167-7672-729d-8d43-653518f3d939",
"last_action_by": "INITIATOR",
"last_updated_time": "2024-05-31T20:32:03.787519Z",
"managed_by": "GROUND_CONTROL",
"memo": "Ordered item never arrived.",
"network": "DECISIONLY",
"payment_rail": "CARD",
"status": "OPEN",
"tenant": "asbght_iujkio",
"timestamp_final_decision": "2024-05-31T20:32:03.787519Z",
"transaction_id": "018fc168-3ce3-7839-8cd3-a653bc4aa9bc",
"action_history": [
{
"id": "018fc16e-0355-7cea-bf80-b0eddd63d48a",
"creation_time": "2024-05-28T22:58:30.396998Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "EVIDENCE_GATHERING",
"status": "COMPLETED",
"external_reference_id": "018fc167-7672-729d-8d43-653518f3d939"
},
{
"id": "018fcb2e-1111-7cea-bf80-b0eddd63d48a",
"creation_time": "2024-05-29T09:00:00.000000Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "CHARGEBACK",
"status": "SUBMITTED"
},
{
"id": "018fcb2f-50f4-7514-a82d-e6143f57f25f",
"creation_time": "2024-05-29T10:00:00.000000Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "PROVISIONAL_CREDIT",
"status": "SUBMITTED"
},
{
"id": "018e62f5-8141-708f-a924-c706d82876a5",
"creation_time": "2024-05-30T20:32:03.787519Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "REPRESENTMENT",
"status": "SUBMITTED",
"supporting_doc_id": "018fcb30-90a1-7221-9160-28917c2cfc2d",
"message": "Merchant provided proof of delivery"
},
{
"id": "018e62fa-4e7a-7814-9634-e130c66f6444",
"creation_time": "2024-05-31T20:32:03.787519Z",
"tenant": "asbght_iujkio",
"payment_rail": "CARD",
"action": "DISPUTE",
"status": "LOST"
}
],
"available_actions": [
{
"action": "PROVISIONAL_CREDIT",
"state": "REVERSE"
}
],
"lifecycle_state": "REPRESENTMENT",
"network_eligibility": {
"is_digital_wallet_token": false,
"is_three_ds": false,
"is_network_eligibility_overridden": false
},
"reason_code": "GOODS_OR_SERVICES_NOT_PROVIDED",
"evidence": {
"merchant_contact": {
"was_attempted": true,
"date": "2024-05-20",
"was_successful": false,
"description": "Called merchant support; no tracking information provided."
},
"delivery": {
"expected_date": "2024-05-15"
}
},
"acquirer_reference_number": "52405245204967729855476"
}
```
### 7. Close Dispute
After the provisional credit has been reversed (`credit_status` = `NONE`), close the dispute.
##### Request
```bash Shell theme={"system"}
curl \
-X PATCH \
$baseurl/v1/disputes/{$dispute_id} \
-H "Authorization: Bearer $apiKey" \
--json '
{
"status": "CLOSED"
}
'
```
## Simulating Network Dispute Actions
**Sandbox Environment Only**
Simulations are intended for use only in the testing environment to simulate a network action on a dispute. For dispute creation and issuer actions, follow the steps listed in the [Transaction Disputes API Guide](/v2/docs/transaction-disputes).
In production, acquirer / network actions are applied automatically as the case progresses with the card network. In sandbox, use simulations to advance those same steps without waiting on a live network response.
Network dispute simulations must follow the logical order of the dispute lifecycle — for example, after a chargeback is submitted you can simulate a representment. For a complete list of network actions, see [Acquirer / Network Actions](/v2/docs/card-transaction-disputes#acquirer--network-actions).
To progress a dispute to the next lifecycle and simulate a network response, use [`POST /v1/disputes/simulations/{$dispute_id}/actions`](/v2/reference/simulatedisputeaction). Request bodies use `state` (for example `CREATE`); matching entries in `action_history` use `status` (for example `SUBMITTED`).
```bash Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/disputes/simulations/{$dispute_id}/actions \
-H "Authorization: Bearer $apiKey" \
--json '
{
"payment_rail": "CARD",
"action": "REPRESENTMENT",
"state": "CREATE",
"message": "Merchant provided proof of delivery"
}
'
```
# Card Transactions
Source: https://docs.synctera.com/v2/docs/card-transactions
A card transaction is defined as a transaction that occurs on a customer issued card.
## Card Transactions
All Synctera payment types utilize the common `/transactions` resource - see [API reference](/v2/reference/gettransactionsbatchpayments) for details.
### Transaction Flows
The flow of a transaction is split into two major components:
### Funding Request
Funding requests are initiated at the time of card usage. When a cardholder initiates a transaction, the merchant sends a request to the network. The network then sends a funding request to Synctera, expecting an approved or denied response. A [Pending Transaction](/v2/reference/gettransactionsbatchpayments) is created when the funding request is received, which publishes a [webhook](/v2/docs/webhooks-guide)) on the `transaction.pending.created` topic with initial status `PENDING`.
Synctera then makes a determination on whether to approve or decline the funding request. See [declined transactions](#declined-transactions) for more details.
While evaluating the funding request, the [Pending Transaction](/v2/reference/gettransactionsbatchpayments) may be updated with supplemental data gathered during the process. A `transaction.pending.upated` [webhook](/v2/docs/webhooks-guide) is published when the transaction is updated. For example, [enhanced transaction](#enhanced-transactions) information may be added.
#### Funding Request Flow
```mermaid mermaid theme={"system"}
%%{init: {"fontFamily": "sans-serif"}}%%
sequenceDiagram
Customer->>Merchant: Initiate transaction
Merchant->>Network: Request
Network->>Synctera: Funding request
Synctera->>FinTech: Pending transaction created webhook
Synctera->>Synctera: Evaluate funding request
Synctera->>FinTech: Pending transaction updated webhook
Synctera-->>Network: Response
Network-->>Merchant: Response
```
In addition, the FinTech can elect to participate in the funding request authorization flow - see [FinTech in the auth flow](#fintech-in-the-auth-flow) - which adds an additional decision point to the evaluation.
### Clearing
The transaction is not fully complete until it has cleared. The clearing process causes a transaction to change from **pending** to **posted**. This is when money actually moves. Note that **pending** and **posted** transactions are distinct resources - see [API reference](/v2/reference/gettransactionsbatchpayments) for more details.
After the funding request is approved - sometimes hours - the merchant initiates a request to the network for retrieving the funds for all of their pending transactions. Synctera receives the clearing notification, updates the [Pending Transaction](/v2/reference/gettransactionsbatchpayments) and creates a [Posted Transaction](/v2/reference/gettransactionsbatchpayments).
Two [webhooks](/v2/docs/webhooks-guide) are published when clearing is complete. First, `transaction.pending.updated` when [Pending Transaction](/v2/reference/gettransactionsbatchpayments) status is changed to `CLEARED`. Second, `transaction.posted.created` when the [Posted Transaction](/v2/reference/gettransactionsbatchpayments) is created.
#### Clearing Flow
```mermaid mermaid theme={"system"}
%%{init: {"fontFamily": "sans-serif"}}%%
sequenceDiagram
Merchant->>Network: Request
Network->>Synctera: Notification
Synctera->>FinTech: Pending transaction updated webhook
Synctera->>FinTech: Posted transaction created webhook
```
### Declined Transactions
Transactions may be declined for various reasons, including:
* Insufficient funds
* Suspected fraud
* Customer or account in bad standing
* Gateway ([FinTech in the auth flow](#fintech-in-the-auth-flow))
* [Network Stand-In](#network-stand-in-scenario)
* Address Verification Service (AVS) did not match address on file
When a transaction is declined, the status is updated to `DECLINED`. In most cases, a `transaction.pending.updated` will be issued to signal change of status. However, in the case of network declination, there will only be a `transaction.pending.created` webhook with initial status of `DECLINED`.
The `data.reason` field of the [Pending Transaction](/v2/reference/gettransactionsbatchpayments) body contains the reason for why the transaction was declined. Below is a list of reasons and their sources:
Customer/account standing:
* `NO_CHECKING_ACCOUNT`
* `NO_SAVINGS_ACCOUNT`
* `NO_CREDIT_ACCOUNT`
* `CLOSED_ACCOUNT`
Fraud:
* `SUSPECTED_FRAUD`
Ledger:
* `INSUFFICIENT_FUNDS`
* `DISABLED_PAYMENT_TYPE`
* `BALANCE_VIOLATION`
* `DUPLICATE_TRANSACTION`
* `SPEND_CONTROL_VIOLATED` (see [Spend Controls](/v2/docs/spend-controls-guide))
Network stand-in:
* `NETWORK_DECLINED`
For network declined transactions, there will be additional details under `data.user_data.network_decline_details` of the [Pending Transaction](/v2/reference/gettransactionsbatchpayments) body.
Gateway:
* `GATEWAY_DECLINED`
* `GATEWAY_ERROR`
Address Verification Service (AVS):
* `ADDRESS_VERIFICATION_FAILED`
General purpose:
* `TRANSACTION_NOT_PERMITTED`
The list of possible decline reasons is subject to grow, so code should be written defensively around processing declined transactions.
### Network Stand-In Scenario
Stand-in scenario happens when a funding request from the network is not responded to in time, so the network must *stand in* to be the decision maker. It is intended to be a fallback mechanism and not to be consistently relied upon, as there is less control over the decision which may result in undesired money movement.
In rare cases, Synctera may never recieve a funding request and only be informed that a transaction has taken place after the fact. In this case, there will only be a [Posted Transaction](/v2/reference/gettransactionsbatchpayments), no [Pending Transaction](/v2/reference/gettransactionsbatchpayments).
### Card Transaction Simulations
Synctera provides a variety of endpoints that simulate card transactions. These endpoints mimic how real transaction will look in a production environment. This gives FinTechs the tools to develop and test their application against the different types of card transactions without any real money movement or financial impact.
All card transaction simulation endpoints are found under the `/cards/transaction_simulations` path - see [API reference](/v2/reference/simulateauthorization) for details. These endpoints can only be used in the [Synctera Sandbox](/v2/reference/need-to-know#sandbox) environment.
This guide will explain how to simulate a selection of typical transaction scenarios.
#### Merchant Information Fields
Many of these endpoints require a `card_acceptor` object, which contains merchant information:
```json JSON theme={"system"}
{
"address": "address",
"city": "city",
"country": "country",
"mcc": "mcc",
"name": "name",
"state": "state",
"zip": "zip"
}
```
The `mcc` (MCC - Merchant Category Code) field is a universal code assigned to merchants for the purpose of classifying the type of goods and services they provide.
In addition, the top level `mid` (MID - Merchant Identification Number) field is sometimes required. This is a unique identifier assigned to all merchants that process card transactions.
For simulations, the content used for `card_acceptor` and `mid` is not neccesarily important. However, merchant information is often crucial for [FinTech in the Auth Flow](#fintech-in-the-auth-flow).
#### 1. Authorization & Clearing
This describes the typical case where a cardholder purchases something from a physical or online merchant. The transaction amount is authorized, then later cleared. To simulate this scenario, an **authorization**, then **clearing** is performed. Note that an **authorization** equates to a [funding request](#funding-request).
To simulate, first call [Simulate authorization](/v2/reference/simulateauthorization), providing the appropriate `card_id`, along with the authorization `amount`. This will generate an **authorization** (funding request), which applies a hold for the given amount of money and creates a [Pending Transaction](/v2/reference/gettransactionsbatchpayments). On a successful call, a transaction `token` is generated. The `token` is needed for clearing.
Next, call [Simulate clearing or refund](/v2/reference/simulateclearing) with the same `amount` as was used in the **authorization** and `original_transaction_id` set to the **authorization** transaction `token`.
#### 2. Authorization Adjustment
In some circumstances, a merchant may want to authorize for a different amount than what is ultimately cleared. For example, at a gas pump, it is common for a customer to pre-authorize up to a certain amount, but only be charged for the amount of gas that was actually pumped. In this case, an initial **authorization** is created for the pre-authorized amount. Once the final sale price is determined, an **authorization advice** is performed to adjust the hold. Like the previous scenario, a final **clearing** is done to capture the funds.
To simulate, first call [Simulate authorization](/v2/reference/simulateauthorization) like in the previous example and retain the transaction `token`.
Next, issue an **authorization advice** with [Simulate authorization advice](/v2/reference/simulateauthorizationadvice), providing the new `amount` and `original_transaction_id` (`token`).
Finally, issue the **clearing** with [Simulate clearing or refund](/v2/reference/simulateclearing), using the final `amount` from the **authorization advice** and `original_transaction_id` set to the transaction `token` from the original **authorization** response.
#### 3. Reversals & Refunds
**Reversals** and **refunds** both refer to the undoing of a previous action, the only difference being the action that is being undone. **Reversals** release an existing **authorization** hold and **refunds** undo a cleared transaction and move funds back to a cardholder's card.
To simulate a **reversal**, an existing **authorization** hold must already exist. To issue the reversal, call [Simulate reversal](/v2/reference/simulatereversal), providing the full **authorization** `amount` and `original_transaction_id` set **authorization** transaction `token` for `original_transaction_id`.
Likewise, to simulate a **refund**, a cleared transaction must already exist. To issue the refund, call [Simulate clearing or refund](/v2/reference/simulateclearing), providing the final `amount` from the **clearing** and the original **authorization** transaction `token` for `original_transaction_id`.
#### 4. Single Message
All above listed scenarios involve **dual message** transactions. Meaning, the transaction occurs in two parts: **authorization**, then **clearing**. However, there are also **single message** transactions, where **authorization** and **clearing** occur in a single action. Common examples include PIN-debit transactions and ATM transactions.
Below are descriptions of the common **single message** simulation endpoint use cases:
* [Simulate financial](/v2/reference/simulatefinancial): This endpoint is used to simulate a PIN-debit transaction - when a cardholder provides their PIN code at the time of purchase. The `amount` and `card_id` must be provided.
* [Simulate ATM withdrawal](/v2/reference/simulatewithdrawal): This endpoint simulates a cash withdrawal from an ATM (Automated Teller Machine). Similarly, `amount` and `card_id` must be provided.
#### 5. L2L3 data
Level 2 and Level 3 card processing data provide detailed information about a transaction. These additional fields within a payment message offer a comprehensive view of the specific items or services being paid for.
To simulate a l2l3, an existing cleared transaction must already exists. Meaning, the transaction occurs in three parts: authorization, clearing then l2l3.
To issue a l2l3, call [Simulate l2l3](/v2/reference/simulatel2l3), providing the clearing transaction token for original\_transaction\_id and the l2l3 data.
### Enhanced Transactions
By default, transaction metadata (merchant name, location, category, etc.) is provided by the network - found in the transaction `data.user_data` object. For the most part, this information is limited and often inconsistently formatted.
If desired, a card product can be configured with a supported third party provider to add enhanced metadata to each transaction. Synctera's current supported enhanced transaction provider is [MX](https://www.mx.com).
For example, given the following network provided merchant name and MCC (merchant category code):
```json JSON theme={"system"}
{
"mcc": "4816",
"name": "EIG*HOSTGATOR.COM"
}
```
Enhanced transaction can provide a cleanly formatted name and consistent category:
```json JSON theme={"system"}
{
"category": "Hosting",
"enhanced_description": "HostGator"
}
```
Enhanced transaction information is found in the transaction `data.user_data.enhanced_transaction` object. `enhanced_raw` contains the complete set of data received from the enhanced transaction provider.
```json JSON theme={"system"}
{
"category": "Hosting",
"enhanced_description": "HostGator",
"enhanced_raw": [
{
"amount": 12.75,
"categorized_by": 13,
"category": "Hosting",
"category_guid": "CAT-b74fdd98-4391-8015-eafa-e9ca0fad3bee",
"described_by": 6,
"description": "HostGator",
"extended_transaction_type": null,
"id": "2ffa6d90-e4d0-47e3-a290-17240e7a3ae4",
"is_bill_pay": false,
"is_direct_deposit": false,
"is_expense": null,
"is_fee": null,
"is_income": false,
"is_international": null,
"is_overdraft_fee": false,
"is_payroll_advance": false,
"is_subscription": false,
"memo": "ad0f57f1-f823-4ba7-8563-a1c4aa458371",
"merchant_category_code": 4816,
"merchant_guid": "MCH-dae1c6b5-292b-4d3e-ba15-998ab24a79c2",
"merchant_location_guid": null,
"original_description": "EIG*HOSTGATOR.COM",
"type": "DEBIT"
}
]
}
```
### L2/L3 Data
Some transactions contain Level 2 / Level 3 (L2/L3) data, which provides much more detailed information about the transaction. For details on L2/L3 data, see [this article](/v2/docs/level-2-level-3-data).
When available, L2/L3 data is received from the processor as part of card transaction clearing events/webhooks, i.e. as a transaction transitions from authorized/pending to cleared/posted.
On occasion, for a single transaction with Level 2/Level 3 data, we may receive a clearing event followed by the L2/L3 data several hours later. As a result, a transaction might initially be posted without detailed L2/L3 data, only to be subsequently updated when this additional information becomes available.
When available, L2/L3 data is found in the transaction user\_data.l2l3 object on a posted transaction. Example:
```json JSON theme={"system"}
{
"l2l3": {
"enhanced_data_id": "e3434344d343434dfdf3564645jk4282328032903j323923023u4h434343",
"financial": {
"tax_id": "123456789",
"total_tax_amount_indicator": "D"
},
"fleet_emv": {
"vat_tax_rate": "0",
"service_type": "S",
"odometer_reading": "0000000",
"fuel_net_amount": 1000,
"fuel_gross_amount": 1000,
"fuel_unit_price": "358.9",
"fuel_unit_of_measure": "G",
"fuel_quantity": "2.786",
"expanded_fuel_type": "01",
"type_of_purchase": "3",
"non_fuel_gross_amount": 1625,
"non_fuel_item_details": [
{
"product_code": "ZC"
}
]
},
"inventory_details": [
{
"description": "Edelmann 92397 Power Steering Press",
"item_discount_amount_indicator": "C",
"item_discount_applied_indicator": "N",
"item_extended_amount": 5048,
"item_extended_amount_indicator": "D",
"product_code": "B00J5W6CZQ",
"quantity": "1",
"unit_of_measure": "PCE"
}
]
}
}
```
### FinTech in the Auth Flow
#### Authorization Gateway
An authorization Gateway enables a FinTech to optionally take part in the funding request decision of a card transaction’s authorization cycle. The FinTech receives an authorization request via the configured Gateway to either approve or decline the card transaction based on the FinTech's own business logic.
If the fintech opts not to participate in the auth flow Synctera will use default authorization logic to authorize the transactions. The fintech does not have to participate unless there is additional approval logic they would like to incorporate into the decision process that is not supported by Synctera or proprietary in nature.
The authorization request must be responded to within a firm timeout window of **1.5 seconds**. Synctera will default to declining the funding request if a response is not received within the timeout window.
The information in the authorization request includes, but is not limited to:
* Customer/Account/Card ID's
* Available balance
* Merchant information
```json JSON theme={"system"}
{
"customer_id": "2b9cc6f2-d0bd-4d9d-aa20-5e53355f9469",
"account_id": "0221e0a7-7774-48a4-8521-e678ec09a53a",
"transaction_id": "9b59fc80-9bf5-4749-8dd2-511f183becf2",
"card_id": "6128498a-85a9-4bd8-a3ea-bfe3717b64f6",
"card_format": "PHYSICAL",
"last_four": "1234",
"type": "card_transaction",
"user_transaction_time": "2022-03-25T10:41:01-04:00",
"settlement_date": "0001-01-01T00:00:00Z",
"amount": {
"amount": 100,
"currency": "USD",
"currency_conversion": {
"original_amount": 100,
"conversion_rate": 1,
"original_currency_code": "840",
"original_currency_code_alpha": "USD"
}
},
"balance": {
"available_balance": 4210000
},
"merchant": {
"mid": "4445001899609",
"mcc": "5411",
"name": "WHOLEFDS EGW 101",
"city": "EDGEWATER",
"state": "NJ",
"postal_code": "07020",
"country_code": "USA",
"sub_merchant_id": "",
"payment_facilitator_id": ""
},
"user": {},
"network_fraud": {
"transaction_risk_score": 18
},
"network": "MASTERCARD",
"subnetwork": "",
"dc_sign": "debit",
"pos": {
"pan_entry_mode": "MAG_STRIPE",
"pin_present": false,
"terminal_id": "10000000",
"cvv_presence": "CVV1"
},
"processor": "MARQETA",
"processor_data": {}
}
```
Addionally, if [enhanced transactions](#enhanced-transactions) are enabled for the FinTech, this information will also be included.
To signal an authorization request decision, the FinTech must reply with the appropriate HTTP code:
* HTTP code `200`: **approve** the funding request
* HTTP code `402`: **decline** the funding request
Note that while HTTP code `402` is conventional, any code other than `200` will also be interpreted as a **decline**.
#### FinTech Included in Funding Request Authorization Flow
```mermaid mermaid theme={"system"}
%%{init: {"fontFamily": "sans-serif"}}%%
sequenceDiagram
Customer->>Merchant: Initiate transaction
Merchant->>Network: Request
Network->>Synctera: Funding request
Synctera->>FinTech: Pending transaction created webhook
Synctera->>Synctera: Evaluate funding request
Synctera->>FinTech: Authorization request
FinTech->>FinTech: Evaluate authorization request
FinTech-->>Synctera: Response
Synctera->>FinTech: Pending transaction updated webhook
Synctera-->>Network: Response
Network-->>Merchant: Response
```
#### Sandbox Implementation
1. Create a new Gateway configuration via [Synctera API](/v2/reference/listcards)
2. Generate an authorization [card transaction simulation](#card-transaction-simulations) and ensure the Gateway endpoint successfully recieves and responds to the request
For local testing, [ngrok](https://ngrok.com) or [beeceptor](https://beeceptor.com) can be used to produce a publicly accesable URL that terminates to a local development endpoint.
#### Gateway Endpoint Configuration
To create a Gateway, a valid, publicly accessable URL must be supplied, along with a list of Card Product ID's that will utilize the Gateway. Additionally, customer headers may be supplied that will be included in authorization calls to the Gateway. If not supplied, `active` status is set to `true` by default. Gateways may be turned off by setting this field `false`.
Note that a Card Product may not be configured on more than one active Gateway at a time.
```bash Shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H "Content-Type: application/json" \
$baseurl/v0/cards/gateways \
--data-binary '
{
"active": true,
"url": "https://example.com",
"custom_headers": {
"key": "value"
},
"card_products": [
"41e81dff-05d4-4421-b7f0-149e3a536979",
"e8361f07-1e7d-440c-91bd-def3e18a907f"
]
}'
```
# Card Widgets
Source: https://docs.synctera.com/v2/docs/card-widgets
With the Card Widgets, your customers can view and interact with sensitive card information inside the application.
Synctera provides secure, PCI-compliant widgets that let your customers interact with sensitive card data directly in your application. Since the widgets communicate directly between the client and Synctera, your application does not need to handle sensitive card data, removing the need for PCI certification.
There are four widgets available:
### Reveal Card Widget
Securely display sensitive card information — card number (PAN), security code (CVV), and expiration date.
### Activate Card Widget
Allow customers to activate a physical card by entering their card number and security code.
### Set PIN Widget
Allow customers to set or change their card PIN with secure confirmation.
### Reveal PIN Widget
Securely display a cardholder's existing PIN, with an auto-hide countdown. The PIN is rendered inside an isolated iframe and never exposed to your page.
## Getting Started
Make sure you've got the necessary components in place before integrating widgets with your application.
### API Keys
First, ensure you have your [Synctera API Keys](/v2/docs/dev-setup#sign-up-for-a-synctera-account-and-generate-api-keys) working for your business.
### Mobile Applications
If you're integrating the widgets in a mobile application, you may need to use one of the following web views:
### Content Security Policy
If your application sets a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), you must allow the Synctera widget domains. The widgets load scripts and create iframes from Synctera-hosted origins, so a strict CSP that omits these domains will silently block the widgets from rendering.
Add the following directives for your environment:
```text Sandbox theme={"system"}
script-src https://assets-sandbox.synctera.com;
frame-src https://assets-sandbox.synctera.com;
connect-src https://assets-sandbox.synctera.com https://api-sandbox.synctera.com;
```
```text Production theme={"system"}
script-src https://assets.synctera.com;
frame-src https://assets.synctera.com;
connect-src https://assets.synctera.com https://api.synctera.com;
```
If you use `nonce`-based script policies, the widget's `
```
```javascript React theme={"system"}
import { useEffect } from 'react';
function ActivateCardForm({ widgetToken }) {
useEffect(() => {
const script = document.createElement('script');
script.type = 'module';
script.src = 'https://assets.synctera.com/widgets/activate/v1.1.1/index.js';
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, []);
return (
);
}
```
## Step 2: Get a Widget Token
Request a widget token from your backend using the Synctera API. The widget token is required for the widget to authenticate and submit data.
```bash curl theme={"system"}
curl -X GET "https://api.synctera.com/v2/cards/{card_id}/widget_token?widget_type=ACTIVATE" \
-H "Authorization: Bearer {apiKey}" \
-H "Content-Type: application/json"
```
```javascript Node.js theme={"system"}
const response = await fetch(
`https://api.synctera.com/v2/cards/${cardId}/widget_token?widget_type=ACTIVATE`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
}
);
const { widget_token } = await response.json();
```
Widget tokens expire after **5 minutes** and are scoped to a specific card. Generate a new token on each page load or when the user initiates a new activation.
## Step 3: Add the Widget Component
Add the `` web component to your page:
```html theme={"system"}
```
***
## Configuration Options
| Property | Type | Required | Default | Description |
| --------------- | -------- | -------- | ----------- | -------------------------------------------------------------------------------------- |
| `token` | `string` | Yes | - | Widget token obtained from the API |
| `env` | `string` | Yes | - | Environment: `sandbox` or `production` |
| `theme` | `string` | No | `"default"` | Theme preset: `"default"` or `"night-shift"` |
| `styles` | `string` | No | `{}` | JSON string of [design tokens](/v2/docs/card-widgets-theming) for visual customization |
| `custom-labels` | `string` | No | `{}` | JSON string of custom labels for form fields |
***
## Custom Labels
Customize the labels displayed in the widget:
```html theme={"system"}
```
Available custom label keys:
* `widgetTitle` - Widget header text
* `cardNumberLabel` - Card number field label
* `securityCodeLabel` - CVV field label
* `cardPanPlaceholder` - Card number placeholder
* `cardCvvPlaceholder` - CVV placeholder
* `submitButtonText` - Submit button text
* `submitLoadingText` - Loading state text
***
## Event Handling
The widget dispatches lifecycle events across two phases:
| Phase | Events | Description |
| ------------------ | -------------------------------------- | -------------------------------------------------------------------------------------------- |
| **Initialization** | `load` or `error` (mutually exclusive) | Fires once when the widget first renders and its secure input fields load (or fail to load). |
| **Submission** | `success` or `failure` | Fires after the user submits the form. |
* **`load`** — Widget initialized successfully, all fields are ready for user input.
* **`error`** — Widget failed to initialize (field load failure, network error, or timeout).
* **`success`** — Card activation completed successfully.
* **`failure`** — Card activation failed (incorrect card number, wrong CVV, expired widget token, or API error).
`error` fires only during widget initialization (e.g., a secure input field failed to load). If the widget loads successfully but the user's submission fails — incorrect PAN, wrong CVV, expired token — that triggers `failure`, not `error`.
You can listen for events using either **`addEventListener`** or **callback properties**:
| Event | addEventListener | Callback property |
| ---------------------- | ---------------------------------------- | ----------------------- |
| Initialization success | `widget.addEventListener('load', fn)` | `widget.onLoad = fn` |
| Initialization failure | `widget.addEventListener('error', fn)` | `widget.onError = fn` |
| Submission success | `widget.addEventListener('success', fn)` | `widget.onSuccess = fn` |
| Submission failure | `widget.addEventListener('failure', fn)` | `widget.onFailure = fn` |
### Load Event
Dispatched when all secure input fields have loaded and the widget is fully functional. Use it to hide loading UI or enable dependent controls.
| Property | Type | Description |
| ------------ | -------- | ---------------------------------- |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
### Error Event
Dispatched when the widget fails to initialize. This means one or more secure input fields could not load, and the widget is not functional. Show an error message or retry UI to the user.
| Property | Type | Description |
| -------------- | ---------- | ---------------------------------------------- |
| `instanceId` | `string` | Unique ID for this widget instance |
| `error` | `string` | Human-readable error message (safe to display) |
| `failedFields` | `string[]` | List of field types that failed to load |
```html theme={"system"}
```
The `load` and `error` events are mutually exclusive — exactly one will fire during widget initialization. Always listen for both to handle all scenarios.
### Success and Failure Events
Dispatched after the user submits the activation form. Common `failure` reasons include incorrect card number, wrong CVV, and expired widget token.
The `success` event exposes only non-sensitive status metadata:
| Property | Type | Description |
| ------------ | -------- | ---------------------------------------- |
| `status` | `string` | Submission status returned by the widget |
| `message` | `string` | Optional human-readable success message |
| `instanceId` | `string` | Unique ID for this widget instance |
PAN and CVV are never included in host-page event details.
```html addEventListener theme={"system"}
```
```html Callback Properties theme={"system"}
```
***
## Complete Example
```html Complete HTML Example theme={"system"}
Activate Card
;
return (
);
}
export default ActivateCardForm;
```
# Legacy: Marqeta Widgets
Source: https://docs.synctera.com/v2/docs/card-widgets-legacy
Documentation for the deprecated Marqeta widget integrations.
Marqeta is sunsetting their Marqeta.js widget library. We recommend migrating to [Synctera Widgets](/v2/docs/card-widgets#synctera-widgets-recommended) for new integrations.
The Marqeta widgets documented in this section will continue to work during the transition period, but new integrations should use Synctera Widgets.
## Overview
The legacy Marqeta widgets provide card functionality through two integration methods:
1. **Marqeta.js Library** - For displaying sensitive card data (PAN, CVV, EXP, PIN)
2. **Marqeta Widget URLs** - For card activation and PIN setting via iframes
## Documentation
Step-by-step guide to migrate from Marqeta widgets to Synctera widgets
Complete documentation for Marqeta.js and widget URL integrations
## Why Migrate?
Synctera Widgets offer several advantages over the legacy Marqeta widgets:
| Feature | Marqeta Widgets | Synctera Widgets |
| -------------------- | --------------------------- | ---------------------------- |
| **Integration** | iframe URLs or JS bootstrap | Web Components |
| **Customization** | Limited CSS styling | Full theming + custom labels |
| **Event Handling** | Callback functions | Standard DOM events |
| **Token Management** | Multiple token types | Unified widget token |
| **Future Support** | Being sunset | Actively maintained |
## Recommendation
* **New integrations**: Use [Synctera Widgets](/v2/docs/card-widgets#synctera-widgets-recommended)
* **Existing integrations**: Plan migration using the [Migration Guide](/v2/docs/card-widgets-legacy-migration)
* **Reveal Card functionality**: Use [Synctera Reveal Card Widget](/v2/docs/card-widgets-reveal)
# Marqeta Widgets (Deprecated)
Source: https://docs.synctera.com/v2/docs/card-widgets-legacy-marqeta
Legacy documentation for Marqeta.js widget integrations.
Marqeta is sunsetting their Marqeta.js widget library. For new integrations, use [Synctera Widgets](/v2/docs/card-widgets#synctera-widgets-recommended).
For migration guidance, see the [Migration Guide](/v2/docs/card-widgets-legacy-migration).
Through the [marqeta.js client library](https://www.marqeta.com/docs/developer-guides/using-marqeta-js), you can display the following pieces of sensitive card information for a customer inside your application:
* **PAN** (Primary Account Number)
* **CVV** (Card Verification Value)
* **EXP** (Expiration Date)
* **PIN** (Personal Identification Number - *v2.0.0+*)
Through the [Activate Card and Set PIN Widgets](https://www.marqeta.com/docs/developer-guides/using-activate-card-and-set-pin-widgets), you can interact with your card to activate it or to set card's PIN:
* **Activate Card** (activates a physical card by entering in the card number and CVV)
* **Set PIN** (sets the Card PIN for a newly activated card)
***
## Display Card PAN, CVV, and EXP
Refer to Marqeta's guide [Using Marqeta.js](https://www.marqeta.com/docs/developer-guides/using-marqeta-js) for additional information on the widget configuration and styling.
The steps below describe how the application uses a client access token to show PAN, CVV and Card EXP using the marqeta.js client library:
### Step 1: Load marqeta.js
Load `marqeta.js` into the window object of the browser by adding the following script into the `` tag of the required page:
```html theme={"system"}
```
### Step 2: Get a Client Access Token
Request a client access token for a card from Synctera via the POST request for [/cards/\{card\_id}/client\_token](/v2/reference/getclientaccesstoken) endpoint. Pass `clientAccessToken` to your front-end via SSR or HTTP request. This token expires after five minutes and is only applicable to the given card, so it's a good idea to create a client access token on every page load:
```bash theme={"system"}
curl -X POST "https://api-sandbox.synctera.com/v0/cards/{cardId}/client_token" \
-H "Content-Length: 0" \
-H "Authorization: Bearer {apiKey}"
# Response: {"client_token": ... }
```
### Step 3: Add HTML Elements
Add a separate HTML `div` element to your client page per each piece of the sensitive card data (Card PAN, Card CVV, Card EXP). You can attach this information to any HTML container:
```html theme={"system"}
Synctera
```
### Step 4: Initialize marqeta.js
Initialize `marqeta.js` via bootstrap with token by calling `window.marqeta.bootstrap`. It will create an HTML iframe element inside each HTML `div` element. You can style the `div` elements and inner contents for the *card PAN*, *card CVV*, *card EXP* containers. To do so, use the `showPan` object as described in [Using Marqeta.js > The showPan object](https://www.marqeta.com/docs/developer-guides/using-marqeta-js#_the_showpan_object):
```javascript theme={"system"}
window.marqeta.bootstrap({
clientAccessToken: clientAccessToken,
integrationType: "custom",
component: {
showPan: {
cardPan: { domId: "display-card-pan", format: true },
cardExp: { domId: "display-card-exp", format: true },
cardCvv: { domId: "display-card-cvv" },
},
},
callbackEvents: {
onSuccess: () => console.log("Widget loaded!"),
onFailure: () => console.warn("Widget failed to load."),
},
});
```
### Sequence Diagram
```mermaid theme={"system"}
sequenceDiagram
participant IFE as Integrator Frontend
participant MFE as Marqeta.js
participant IBE as Integrator Backend
participant S as Synctera API
participant M as Marqeta
IFE ->> IBE: Request client access token
IBE ->> S: POST /v0/cards/:card_id/client_token
S ->> M: Get token
M -->> S: Token
S -->> IBE: Response with client_token
IBE -->> IFE: Client access token
IFE ->> MFE: Marqeta.js display card PAN, CVV, and EXP
MFE ->> M: Request sensitive card data
M -->> MFE: Response with sensitive card data
MFE ->> MFE: Render sensitive card data
```
***
## Display Card PIN
For new integrations, use the [Synctera Reveal PIN Widget](/v2/docs/card-widgets-reveal-pin) instead. The steps below describe the deprecated Marqeta `pinReveal` flow.
Loading the PIN must be done in its own call to `window.marqeta.bootstrap`, however you may call two instances simultaneously.
### Step 1: Load marqeta.js and Get Token
Follow the first two steps from [Display Card PAN, CVV, and EXP](#display-card-pan-cvv-and-exp).
### Step 2: Add HTML Elements
Add separate HTML elements to your client page for the card PIN. You can attach this information to any HTML container:
```html theme={"system"}
Synctera
```
### Step 3: Initialize marqeta.js for PIN
Initialize `marqeta.js` via bootstrap with token by calling `window.marqeta.bootstrap`. It will create an HTML iframe element inside each HTML element. You can style the elements and inner contents for the *card PIN* containers. To do so, use the `pinReveal` object as described in [Using Marqeta.js > The pinReveal object](https://www.marqeta.com/docs/developer-guides/using-marqeta-js#_the_pinreveal_object):
```javascript theme={"system"}
window.marqeta.bootstrap({
clientAccessToken: clientAccessToken,
integrationType: "custom",
component: {
pinReveal: {
cardPin: { domId: "display-card-pin" },
toggleCardPin: { domId: "toggle-card-pin", mode: "transparent" },
hidePinTimeout: {
domId: "pin-timeout",
hideTimeout: 10, // A value between 5 and 15
styles: {}, // Requires styles object, can be empty
},
},
},
callbackEvents: {
onSuccess: () => console.log("Widget loaded!"),
onFailure: () => console.warn("Widget failed to load."),
},
});
```
***
## Activate Card and Set PIN Widgets
For new integrations, use the [Synctera Activate Card Widget](/v2/docs/card-widgets-activate) and [Synctera Set PIN Widget](/v2/docs/card-widgets-set-pin) instead.
The **Activate Card** widget and **Set PIN** widget are displayed inside HTML iframe elements, with source URLs provided by the `/cards/card_widget_url` route.
Refer to Marqeta's guide [Using Activate Card and Set PIN Widgets](https://www.marqeta.com/docs/developer-guides/using-activate-card-and-set-pin-widgets) for additional information.
### Step 1: Fetch the Widget URL
On the server, make a request to `/cards/card_widget_url`. For the **Activate Card** widget (widget\_type === 'activate\_card'), you can omit the `card_id` param in the query:
```bash theme={"system"}
curl -X GET "https://api-sandbox.synctera.com/v0/cards/card_widget_url?card_id={cardId}&customer_id={customerId}&account_id={accountId}&widget_type={widgetType}" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {apiKey}"
# Response: {"url": ... }
```
### Step 2: Embed the Widget
Pass the acquired URL to your front-end via SSR or HTTP request. Include the URL into an iframe on your client page. The desired widget will be rendered inside the iframe, allowing the user to input either *card PAN* or *card PIN* and press submit:
```html theme={"system"}
Synctera
```
### Sequence Diagram
```mermaid theme={"system"}
sequenceDiagram
participant IFE as Integrator Frontend
participant IBE as Integrator Backend
participant S as Synctera API
participant M as Marqeta
IFE ->> IBE: Request card widget
IBE ->> S: GET /v0/cards/card_widget_url
S ->> M: Get card widget URL
M -->> S: Card widget URL
S -->> IBE: Response with card_widget_url
IBE -->> IFE: Card widget URL
IFE ->> IFE: Render Activate Card or Set PIN widget
```
***
## Upgrading from Marqeta 1.1.0 to 2.0.0
To take advantage of the PIN reveal feature, you will need to upgrade from Marqeta 1.1.0 to 2.0.0. If you already implemented the Marqeta widget, read this section to quickly upgrade.
1. Replace `https://widgets.marqeta.com/marqetajs/1.1.0/marqeta.min.js` with `https://widgets.marqeta.com/marqetajs/2.0.0/marqeta.min.js`
2. For the config passed to `window.marqeta.bootstrap({ .... })`, the "showPan" field needs to wrapped in a "component" field:
```javascript theme={"system"}
window.marqeta.bootstrap({
// ...other config
component: {
showPan: {
cardPan: { domId: "display-card-pan", format: true },
cardExp: { domId: "display-card-exp", format: true },
cardCvv: { domId: "display-card-cvv" },
},
},
// ...other config
});
```
That's it!
***
## Styling and Customization
As of version 2.0.0 of the Marqeta library allows for some customization of the fields' appearances, including the css hover state. Supported CSS attributes generally include: `color`, `font-family`, `font-size`, `background`, `font-weight` and `letter-spacing`.
For the particulars on styling, please review the [Using Marqeta.js > Concepts](https://www.marqeta.com/docs/developer-guides/using-marqeta-js#_concepts)
From Marqeta's documentation: *"CSS importing schemes such as @import and @url are not supported. Marqeta.js only supports web-safe/system fonts that can be displayed on modern web browsers without a specific download."*
***
## TypeScript Types
We use Typescript at Synctera and find it helpful to type our window properties. See the example typescript below for the `window.marqeta` **version 2.0.0** object provided by `marqeta.js`:
```typescript theme={"system"}
// global.d.ts
declare interface Window {
marqeta: {
bootstrap(params: MarqetaShowPanParams | MarqetaPinRevealParams): void;
}
}
// marqeta.d.ts
interface MarqetaBaseParams {
clientAccessToken: string;
options?: {
// Must be included if using PIN reveal
cardholderVerificationMethod: 'OTHER';
};
integrationType: 'custom';
callbackEvents?: {
onSuccess?: () => void;
onFailure: () => void;
};
}
export interface MarqetaShowPanParams extends MarqetaBaseParams {
component: {
showPan: {
cardPan?: MarqetaShowItem;
cardExp?: MarqetaShowItem;
cardCvv?: Omit;
};
pinReveal?: undefined;
};
}
export interface MarqetaPinRevealParams extends MarqetaBaseParams {
component: {
showPan?: undefined;
pinReveal: {
cardPin: Omit;
toggleCardPin?: {
domId: string;
mode: 'transparent';
onRevealSuccess?: () => void;
onHideSuccess?: () => void;
};
hidePinTimeout?: {
domId: string;
hideTimeout: 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15;
styles: CSSProperties;
onSuccess?: () => void;
onFailure?: () => void;
};
};
};
}
export interface MarqetaShowItem {
domId: string;
format?: boolean;
styles?: {
span?: CSSProperties;
'span:hover'?: CSSProperties;
};
mode?: 'transparent';
onCopySuccess?: () => void;
onCopyFailure?: () => void;
}
```
# Migration Guide: Marqeta to Synctera
Source: https://docs.synctera.com/v2/docs/card-widgets-legacy-migration
Step-by-step guide to migrate from Marqeta widgets to Synctera widgets.
If you are currently using Marqeta widgets for **Activate Card** or **Set PIN** functionality, follow this guide to migrate to Synctera widgets.
For **Reveal Card** functionality (displaying PAN, CVV, and expiration date), the [Synctera Reveal Card Widget](/v2/docs/card-widgets-reveal) is now available.
For **PIN reveal** functionality (the legacy Marqeta `pinReveal` flow), the [Synctera Reveal PIN Widget](/v2/docs/card-widgets-reveal-pin) is now available — see its [migration table](/v2/docs/card-widgets-reveal-pin#migration-from-marqeta-pin-reveal).
## Activate Card Migration
### API Changes
| Marqeta (Old) | Synctera (New) |
| --------------------------------------------------------- | ------------------------------------------------------------ |
| `GET /v0/cards/card_widget_url?widget_type=activate_card` | `POST /v1/cards/{card_id}/widget_token?widget_type=ACTIVATE` |
| Returns `{ url: "..." }` | Returns `{ widget_token: "..." }` |
### Code Changes
**Before (Marqeta):**
```html theme={"system"}
```
**After (Synctera):**
```html theme={"system"}
```
### Backend Changes
**Before (Marqeta):**
```javascript theme={"system"}
// Server-side: Get widget URL
const response = await fetch(
`https://api.synctera.com/v0/cards/card_widget_url?card_id=${cardId}&customer_id=${customerId}&account_id=${accountId}&widget_type=activate_card`,
{
headers: { 'Authorization': `Bearer ${apiKey}` }
}
);
const { url } = await response.json();
// Return URL to frontend
```
**After (Synctera):**
```javascript theme={"system"}
// Server-side: Get widget token
const response = await fetch(
`https://api.synctera.com/v1/cards/${cardId}/widget_token?widget_type=ACTIVATE`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
}
);
const { widget_token } = await response.json();
// Return token to frontend
```
***
## Set PIN Migration
### API Changes
| Marqeta (Old) | Synctera (New) |
| --------------------------------------------------- | ----------------------------------------------------------- |
| `GET /v0/cards/card_widget_url?widget_type=set_pin` | `POST /v1/cards/{card_id}/widget_token?widget_type=SET_PIN` |
| Returns `{ url: "..." }` | Returns `{ widget_token: "..." }` |
### Code Changes
**Before (Marqeta):**
```html theme={"system"}
```
**After (Synctera):**
```html theme={"system"}
```
### Backend Changes
**Before (Marqeta):**
```javascript theme={"system"}
// Server-side: Get widget URL
const response = await fetch(
`https://api.synctera.com/v0/cards/card_widget_url?card_id=${cardId}&customer_id=${customerId}&account_id=${accountId}&widget_type=set_pin`,
{
headers: { 'Authorization': `Bearer ${apiKey}` }
}
);
const { url } = await response.json();
// Return URL to frontend
```
**After (Synctera):**
```javascript theme={"system"}
// Server-side: Get widget token
const response = await fetch(
`https://api.synctera.com/v1/cards/${cardId}/widget_token?widget_type=SET_PIN`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
}
);
const { widget_token } = await response.json();
// Return token to frontend
```
***
## Key Differences
### Event Handling
**Marqeta** widgets used postMessage or URL callbacks. **Synctera** widgets dispatch standard DOM events:
```javascript theme={"system"}
// Synctera widgets use standard DOM events
widget.addEventListener('success', (event) => {
console.log('Success:', event.detail);
});
widget.addEventListener('failure', (event) => {
console.error('Error:', event.detail);
});
```
### Customization
**Marqeta** widgets had limited customization. **Synctera** widgets support themes and custom labels:
```html theme={"system"}
```
### Token Expiration
Both widget types use tokens that expire. Generate a new token on each page load:
```javascript theme={"system"}
// Recommended: Fetch token when component mounts
async function initializeWidget() {
const { widget_token } = await fetchWidgetToken(cardId);
document.querySelector('activate-card').setAttribute('token', widget_token);
}
```
***
## Migration Checklist
* [ ] Update backend API calls from `/v0/cards/card_widget_url` to `/v1/cards/{card_id}/widget_token`
* [ ] Change HTTP method from `GET` to `POST` for token endpoint
* [ ] Add widget script to your HTML (`
```
```javascript React theme={"system"}
import { useEffect } from 'react';
function RevealCard({ widgetToken }) {
useEffect(() => {
const script = document.createElement('script');
script.type = 'module';
script.src = 'https://assets.synctera.com/widgets/reveal-card/v1.1.1/index.js';
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, []);
return (
);
}
```
## Step 2: Get a Widget Token
Request a widget token for revealing card details:
```bash curl theme={"system"}
curl -X GET "https://api.synctera.com/v2/cards/{card_id}/widget_token?widget_type=REVEAL" \
-H "Authorization: Bearer {apiKey}" \
-H "Content-Type: application/json"
```
```javascript Node.js theme={"system"}
const response = await fetch(
`https://api.synctera.com/v2/cards/${cardId}/widget_token?widget_type=REVEAL`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
}
);
const { widget_token } = await response.json();
```
Widget tokens expire after **5 minutes** and are scoped to a specific card. Generate a new token on each page load or when the user initiates a new reveal action.
## Step 3: Add the Widget Component
Add the `` web component to your page:
```html theme={"system"}
```
***
## Configuration Options
| Property | Type | Required | Default | Description |
| ----------------- | --------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------- |
| `token` | `string` | Yes | - | Widget token obtained from the API |
| `env` | `string` | Yes | - | Environment: `sandbox` or `production` |
| `theme` | `string` | No | `"default"` | Theme preset: `"default"` or `"night-shift"` |
| `styles` | `string` | No | `{}` | JSON string of [design tokens](/v2/docs/card-widgets-theming) for visual customization |
| `card-side` | `string` | No | `"front"` | Which side to show initially: `"front"` or `"back"` |
| `card-form` | `string` | No | `"physical"` | Card form factor: `"physical"` or `"virtual"` |
| `card-type` | `string` | No | `""` | Card type label displayed on the card (e.g., `"DEBIT"`, `"CREDIT"`) |
| `last-four` | `string` | No | `""` | Last 4 digits of PAN, shown on the card front |
| `emboss-name` | `string` | No | `""` | Cardholder name displayed on the card front (line 1) |
| `emboss-name-two` | `string` | No | `""` | Optional second emboss line displayed below the cardholder name in a smaller font (e.g., business name) |
| `card-network` | `string` | No | `"visa"` | Card network: `"visa"` or `"mastercard"` |
| `show-toggle` | `boolean` | No | `false` | Renders a built-in toggle to reveal/hide card details |
| `toggle-label` | `string` | No | `""` | Custom label text for the built-in toggle |
| `custom-labels` | `string` | No | `"{}"` | JSON string of custom labels |
### Card Side and Form
The widget renders a visual card with front and back views:
```html Front Side (default) theme={"system"}
```
```html Back Side theme={"system"}
```
```html Virtual Card theme={"system"}
```
### Built-in Toggle
Set `show-toggle` to render a toggle switch below the card that triggers the card reveal:
```html theme={"system"}
```
### Custom Labels
Customize the labels displayed in the widget:
```html theme={"system"}
```
Available custom label keys:
* `panLabel` - Card number label (back side)
* `cvvLabel` - CVV label (back side)
* `expiryLabel` - Expiration date label (back side)
* `retryButtonText` - Error state retry button text
* `toggleLabel` - Built-in toggle label text
* `cardholderPlaceholder` - Placeholder text when `emboss-name` is empty
***
## Event Handling
The widget dispatches events for both initialization and card reveal outcomes:
* **`load`** — The widget finished initializing and is ready to use.
* **`error`** — The widget failed to initialize (network/CSP failure or handshake timeout).
* **`success`** — Card details retrieved successfully.
* **`failure`** — Card reveal failed (API error, timeout, etc.).
* **`copy`** — A card field value was copied to the clipboard.
You can subscribe with `addEventListener` or by assigning the matching callback property:
| Event | `addEventListener` | Callback property |
| ------------------------- | ---------------------------------------- | --------------------------- |
| Initialization success | `widget.addEventListener('load', fn)` | `widget.onLoad = fn` |
| Initialization failure | `widget.addEventListener('error', fn)` | `widget.onError = fn` |
| Reveal success | `widget.addEventListener('success', fn)` | `widget.onSuccess = fn` |
| Reveal failure | `widget.addEventListener('failure', fn)` | `widget.onFailure = fn` |
| Field copied to clipboard | `widget.addEventListener('copy', fn)` | *not exposed as a property* |
### Load Event
Dispatched once when the widget has finished initializing and is ready to use. Use this to reveal the widget UI only after it's ready, or to dismiss a loading placeholder. Fires exactly once per iframe lifecycle.
| Property | Type | Description |
| ------------ | -------- | ---------------------------------- |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
### Error Event
Dispatched once when the widget fails to initialize — either because the underlying iframe could not load (network failure, blocked by Content Security Policy, etc.) or because the iframe loaded but never completed its handshake within 5 seconds. This is distinct from the `failure` event, which signals that a card reveal API call failed *after* the widget was already running.
| Property | Type | Description |
| -------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `error` | `string` | Human-readable error message (safe to display to end users) |
| `failedFields` | `string[]` | Logical surfaces that failed to initialize. The Reveal Card widget has a single display surface, so this is always `['display-controller']`. |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
### Success Event
Dispatched when card details are successfully retrieved and rendered inside the widget's secure iframe. Sensitive card data (PAN, CVV, expiration) is never exposed to the host page.
| Property | Type | Description |
| ------------ | -------- | ---------------------------------- |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
### Failure Event
Dispatched when the card reveal API call fails.
| Property | Type | Description |
| ------------ | -------- | ---------------------------------- |
| `error` | `string` | Human-readable error message |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
### Copy Event
Dispatched when a user copies a card field value (card number, security code, or expiration) to the clipboard using the copy buttons inside the widget. The actual clipboard operation happens within the secure iframe — no PCI data is exposed in the event.
| Property | Type | Description |
| ------------ | -------- | ---------------------------------- |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
***
## Public Methods
The widget exposes methods for programmatic control. These are useful when you want to control the reveal behavior externally instead of using the built-in toggle.
| Method | Description |
| --------------------- | -------------------------------------- |
| `requestCardReveal()` | Trigger the card reveal API call |
| `toggleCardSide()` | Flip between front and back views |
| `setCardSide(side)` | Set card side to `"front"` or `"back"` |
| `refresh()` | Reset state and re-fetch card details |
```html theme={"system"}
```
***
## Complete Example
```html Complete HTML Example theme={"system"}
Card Reveal
;
return (
);
}
export default RevealCardView;
```
***
## Migration from card-reveal-widget
The Reveal Card widget was previously named ``. It has been renamed to `` for consistency with the other Synctera widgets (``, ``).
### What changed
1. **CDN URL**: The script URL changed from `widgets/card-reveal/v1.0.0/index.js` to `widgets/reveal-card/v1.1.1/index.js`
2. **Element name**: The custom element changed from `` to ``
3. **Existing public methods and events are unchanged** (`requestCardReveal()`, `toggleCardSide()`, `setCardSide()`, `refresh()`, `success`, `failure`, `copy`)
4. **New initialization events**: `` adds `load` and `error` events (plus matching `onLoad` / `onError` callback properties) so you can react to the widget becoming ready or failing to initialize — see [Event Handling](#event-handling). These events are additive and existing integrations continue to work without changes.
### Backwards compatibility
The new widget script registers both `` (recommended) and `` (deprecated alias). If you load the new CDN URL, your existing `` elements will continue to work, but a deprecation warning will be logged to the browser console.
### Migration steps
1. Update the script tag to use the new CDN URL
2. Replace `` with `` in your HTML
3. Update any `document.querySelector('card-reveal-widget')` or `document.createElement('card-reveal-widget')` calls
The old CDN URL (`widgets/card-reveal/v1.0.0/index.js`) is frozen and will not receive updates. Switch to the new URL to receive future improvements and fixes.
# Reveal PIN Widget
Source: https://docs.synctera.com/v2/docs/card-widgets-reveal-pin
Securely display a cardholder's PIN directly in your application, with an auto-hide countdown.
The Reveal PIN widget allows you to securely display a card's PIN directly in your application. The PIN is fetched, rendered, and auto-hidden entirely inside an isolated Synctera-origin iframe — it is never exposed to your page, never included in any event, and never logged — helping reduce your PCI scope.
This is the Synctera replacement for the legacy Marqeta PIN reveal (`pinReveal`) flow. For the deprecated Marqeta integration, see [Marqeta Widgets](/v2/docs/card-widgets-legacy-marqeta#display-card-pin).
## Step 1: Load the Widget Script
Load the Reveal PIN widget script into your page:
```html HTML theme={"system"}
```
```javascript React theme={"system"}
import { useEffect } from 'react';
function RevealPin({ widgetToken }) {
useEffect(() => {
const script = document.createElement('script');
script.type = 'module';
script.src = 'https://assets.synctera.com/widgets/reveal-pin/v1.0.0/index.js';
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, []);
return (
);
}
```
## Step 2: Get a Widget Token
Request a widget token scoped to revealing the card's PIN. Use the `GET_PIN` widget type:
```bash curl theme={"system"}
curl -X GET "https://api.synctera.com/v2/cards/{card_id}/widget_token?widget_type=GET_PIN" \
-H "Authorization: Bearer {apiKey}" \
-H "Content-Type: application/json"
```
```javascript Node.js theme={"system"}
const response = await fetch(
`https://api.synctera.com/v2/cards/${cardId}/widget_token?widget_type=GET_PIN`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
}
);
const { widget_token } = await response.json();
```
Widget tokens expire after **5 minutes** and are scoped to a specific card. Generate a new token on each page load or when the user initiates a new reveal action.
## Step 3: Add the Widget Component
Add the `` web component to your page:
```html theme={"system"}
```
By default the widget renders a placeholder (obscured dots) and waits for a reveal to be triggered — either by the built-in **Show PIN** button (set `show-toggle`) or by calling [`requestPin()`](#public-methods) from your own UI. Once revealed, the PIN displays with a live auto-hide countdown and is cleared automatically when the countdown ends.
***
## Configuration Options
| Property | Type | Required | Default | Description |
| ------------------- | --------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `token` | `string` | Yes | - | Widget token obtained from the API (`GET_PIN` type) |
| `env` | `string` | Yes | - | Environment: `sandbox` or `production` |
| `theme` | `string` | No | `"default"` | Theme preset: `"default"` or `"night-shift"` |
| `styles` | `string` | No | `"{}"` | JSON string of [design tokens](/v2/docs/card-widgets-theming) for visual customization |
| `auto-hide-seconds` | `number` | No | `5` | Seconds the PIN stays visible before it auto-hides, with a live countdown. Set to `0` to disable auto-hide (see PCI note below). |
| `show-toggle` | `boolean` | No | `false` | Renders a built-in **Show PIN** button inline with the obscured dots |
| `toggle-label` | `string` | No | `"Show PIN"` | Custom label text for the built-in button |
| `custom-labels` | `string` | No | `"{}"` | JSON string of custom labels |
### Auto-hide Countdown
The PIN auto-hides after `auto-hide-seconds` (default `5`), mirroring the legacy Marqeta `hidePinTimeout` behavior. This is a PCI safeguard, not just a UX nicety: it limits how long the PIN is on screen.
```html theme={"system"}
```
Setting `auto-hide-seconds="0"` disables auto-hide entirely: the PIN stays visible with no countdown until the widget is torn down or the PIN is re-requested. This weakens your PCI posture — only disable it deliberately.
### Built-in Button
Set `show-toggle` to render a built-in **Show PIN** button next to the obscured dots. When omitted, you control the reveal yourself by calling [`requestPin()`](#public-methods).
```html theme={"system"}
```
### Custom Labels
Customize the labels displayed in the widget:
```html theme={"system"}
```
Available custom label keys:
* `pinLabel` - Label shown above the PIN (default: none — no label is rendered unless you provide one)
* `toggleLabel` - Built-in button label (default: `"Show PIN"`). The dedicated `toggle-label` attribute takes precedence if both are set.
* `retryButtonText` - Error state retry button text (default: `"Try Again"`)
***
## Event Handling
The widget dispatches events for both initialization and PIN reveal outcomes:
* **`load`** — The widget finished initializing and is ready to use.
* **`error`** — The widget failed to initialize (network/CSP failure or handshake timeout).
* **`success`** — PIN retrieved and rendered successfully inside the secure iframe.
* **`failure`** — PIN reveal failed (API error, timeout, etc.).
The Reveal PIN widget does **not** emit a `copy` event. The PIN cannot be copied to the clipboard and text selection is disabled inside the widget — both are intentional PCI safeguards.
You can subscribe with `addEventListener` or by assigning the matching callback property:
| Event | `addEventListener` | Callback property |
| ---------------------- | ---------------------------------------- | ----------------------- |
| Initialization success | `widget.addEventListener('load', fn)` | `widget.onLoad = fn` |
| Initialization failure | `widget.addEventListener('error', fn)` | `widget.onError = fn` |
| Reveal success | `widget.addEventListener('success', fn)` | `widget.onSuccess = fn` |
| Reveal failure | `widget.addEventListener('failure', fn)` | `widget.onFailure = fn` |
### Load Event
Dispatched once when the widget has finished initializing and is ready to use. Use this to reveal the widget UI only after it's ready, or to dismiss a loading placeholder. Fires exactly once per iframe lifecycle.
| Property | Type | Description |
| ------------ | -------- | ---------------------------------- |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
### Error Event
Dispatched once when the widget fails to initialize — either because the underlying iframe could not load (network failure, blocked by Content Security Policy, etc.) or because the iframe loaded but never completed its handshake within 5 seconds. This is distinct from the `failure` event, which signals that a PIN reveal API call failed *after* the widget was already running.
| Property | Type | Description |
| -------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `error` | `string` | Human-readable error message (safe to display to end users) |
| `failedFields` | `string[]` | Logical surfaces that failed to initialize. The Reveal PIN widget has a single display surface, so this is always `['display-controller']`. |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
### Success Event
Dispatched when the PIN is successfully retrieved and rendered inside the widget's secure iframe. The PIN itself is **never** included in the event — only metadata.
| Property | Type | Description |
| ------------ | -------- | ---------------------------------- |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
### Failure Event
Dispatched when the PIN reveal API call fails.
| Property | Type | Description |
| ------------ | -------- | ---------------------------------- |
| `error` | `string` | Human-readable error message |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
***
## Public Methods
The widget exposes methods for programmatic control. These are useful when you want to control the reveal behavior externally instead of using the built-in button (`show-toggle`).
| Method | Description |
| -------------- | ---------------------------------- |
| `requestPin()` | Trigger the PIN reveal API call |
| `refresh()` | Reset state and re-request the PIN |
```html theme={"system"}
```
***
## PCI Considerations
The Reveal PIN widget is designed to keep the PIN out of your PCI scope:
* The PIN is fetched and rendered **entirely inside the Synctera-origin iframe**. It never crosses the iframe boundary to your page.
* The PIN is **never included in any event** (`success` carries only an `instanceId`) and is **never logged**.
* The PIN **cannot be copied** to the clipboard, and text selection is disabled, so it can't be highlighted and copied.
* The PIN **auto-hides** after `auto-hide-seconds` and is cleared from the DOM and memory when hidden.
***
## Complete Example
```html Complete HTML Example theme={"system"}
Reveal PIN
;
return (
);
}
export default RevealPinView;
```
***
## Migration from Marqeta PIN Reveal
If you previously displayed the PIN using the Marqeta.js `pinReveal` component, the Reveal PIN widget is the modern, Synctera-hosted replacement.
| Marqeta (Old) | Synctera (New) |
| -------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `marqeta.min.js` + `window.marqeta.bootstrap({ component: { pinReveal: ... } })` | `` web component |
| `POST /v0/cards/{card_id}/client_token` | `GET /v2/cards/{card_id}/widget_token?widget_type=GET_PIN` |
| `cardholderVerificationMethod: 'OTHER'` (manual config) | Handled internally — no configuration needed |
| `hidePinTimeout` (5–15s) | `auto-hide-seconds` (default `5`) |
| `toggleCardPin` | `show-toggle` / `requestPin()` |
| `onSuccess` / `onFailure` callbacks | `load` / `error` / `success` / `failure` DOM events |
See the [Migration Guide](/v2/docs/card-widgets-legacy-migration) for the Activate Card and Set PIN equivalents.
# Set PIN Widget
Source: https://docs.synctera.com/v2/docs/card-widgets-set-pin
Allow your customers to set or change their card PIN with secure confirmation.
The Set PIN widget allows your customers to set or change their card PIN by entering and confirming a new PIN.
## Step 1: Load the Widget Script
Load the Set PIN widget script into your page:
```html HTML theme={"system"}
```
```javascript React theme={"system"}
import { useEffect } from 'react';
function SetPinForm({ widgetToken }) {
useEffect(() => {
const script = document.createElement('script');
script.type = 'module';
script.src = 'https://assets.synctera.com/widgets/set-pin/v1.1.1/index.js';
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, []);
return (
);
}
```
## Step 2: Get a Widget Token
Request a widget token for setting the PIN:
```bash curl theme={"system"}
curl -X GET "https://api.synctera.com/v2/cards/{card_id}/widget_token?widget_type=SET_PIN" \
-H "Authorization: Bearer {apiKey}" \
-H "Content-Type: application/json"
```
```javascript Node.js theme={"system"}
const response = await fetch(
`https://api.synctera.com/v2/cards/${cardId}/widget_token?widget_type=SET_PIN`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
}
);
const { widget_token } = await response.json();
```
Widget tokens expire after **5 minutes** and are scoped to a specific card. Generate a new token on each page load or when the user initiates a new PIN change.
## Step 3: Add the Widget Component
Add the `` web component to your page:
```html theme={"system"}
```
***
## Configuration Options
| Property | Type | Required | Default | Description |
| --------------- | -------- | -------- | ----------- | -------------------------------------------------------------------------------------- |
| `token` | `string` | Yes | - | Widget token obtained from the API |
| `env` | `string` | Yes | - | Environment: `sandbox` or `production` |
| `theme` | `string` | No | `"default"` | Theme preset: `"default"` or `"night-shift"` |
| `styles` | `string` | No | `{}` | JSON string of [design tokens](/v2/docs/card-widgets-theming) for visual customization |
| `custom-labels` | `string` | No | `{}` | JSON string of custom labels for form fields |
***
## Custom Labels
Customize the labels displayed in the widget:
```html theme={"system"}
```
Available custom label keys:
* `widgetTitle` - Widget header text
* `pinLabel` - PIN field label
* `confirmPinLabel` - Confirm PIN field label
* `pinPlaceholder` - PIN field placeholder
* `confirmPinPlaceholder` - Confirm PIN placeholder
* `submitButtonText` - Submit button text
* `submitLoadingText` - Loading state text
***
## Event Handling
The widget dispatches lifecycle events across two phases:
| Phase | Events | Description |
| ------------------ | -------------------------------------- | -------------------------------------------------------------------------------------------- |
| **Initialization** | `load` or `error` (mutually exclusive) | Fires once when the widget first renders and its secure input fields load (or fail to load). |
| **Submission** | `success` or `failure` | Fires after the user submits the form. |
* **`load`** — Widget initialized successfully, all fields are ready for user input.
* **`error`** — Widget failed to initialize (field load failure, network error, or timeout).
* **`success`** — PIN was set successfully.
* **`failure`** — PIN setting failed (PINs do not match, expired widget token, or API error).
`error` fires only during widget initialization (e.g., a secure input field failed to load). If the widget loads successfully but the user's submission fails — mismatched PINs, expired token — that triggers `failure`, not `error`.
You can listen for events using either **`addEventListener`** or **callback properties**:
| Event | addEventListener | Callback property |
| ---------------------- | ---------------------------------------- | ----------------------- |
| Initialization success | `widget.addEventListener('load', fn)` | `widget.onLoad = fn` |
| Initialization failure | `widget.addEventListener('error', fn)` | `widget.onError = fn` |
| Submission success | `widget.addEventListener('success', fn)` | `widget.onSuccess = fn` |
| Submission failure | `widget.addEventListener('failure', fn)` | `widget.onFailure = fn` |
### Load Event
Dispatched when all secure input fields have loaded and the widget is fully functional. Use it to hide loading UI or enable dependent controls.
| Property | Type | Description |
| ------------ | -------- | ---------------------------------- |
| `instanceId` | `string` | Unique ID for this widget instance |
```html theme={"system"}
```
### Error Event
Dispatched when the widget fails to initialize. This means one or more secure input fields could not load, and the widget is not functional. Show an error message or retry UI to the user.
| Property | Type | Description |
| -------------- | ---------- | ---------------------------------------------- |
| `instanceId` | `string` | Unique ID for this widget instance |
| `error` | `string` | Human-readable error message (safe to display) |
| `failedFields` | `string[]` | List of field types that failed to load |
```html theme={"system"}
```
The `load` and `error` events are mutually exclusive — exactly one will fire during widget initialization. Always listen for both to handle all scenarios.
### Success and Failure Events
Dispatched after the user submits the PIN form. Common `failure` reasons include mismatched PINs and expired widget token.
The `success` event exposes only non-sensitive status metadata:
| Property | Type | Description |
| ------------ | -------- | ---------------------------------------- |
| `status` | `string` | Submission status returned by the widget |
| `message` | `string` | Optional human-readable success message |
| `instanceId` | `string` | Unique ID for this widget instance |
PIN values are never included in host-page event details.
```html addEventListener theme={"system"}
```
```html Callback Properties theme={"system"}
```
***
## Complete Example
```html Complete HTML Example theme={"system"}
Set Card PIN
;
return (
);
}
export default SetPinForm;
```
# Widget Theming & Design Tokens
Source: https://docs.synctera.com/v2/docs/card-widgets-theming
Customize widget appearance with design tokens — colors, fonts, border radius, and more — using the styles JSON attribute.
All Synctera card widgets support runtime theming through the `styles` attribute. Pass a JSON string of design tokens to customize colors, typography, border radius, and more — no widget redeployment needed.
## Quick Start
Set a few brand tokens to theme any widget:
```html theme={"system"}
```
The `styles` attribute follows the same pattern as `custom-labels` — a JSON string on the HTML element.
## How It Works
The widget parses the JSON, resolves tokens against built-in defaults, and applies them as CSS custom properties (`--gizmo-*`) at runtime. Tokens propagate through the widget's Shadow DOM and are securely bridged into PCI-compliant iframes via URL parameters at creation time.
### Interaction with `theme`
The `theme` attribute (`"default"` or `"night-shift"`) sets a base preset. The `styles` attribute overrides individual tokens on top of it. If both are set, `styles` values take precedence per-token.
```html theme={"system"}
```
***
## Token Reference
Tokens are organized into two tiers. **Tier 1** (semantic tokens) are the primary styling API — setting 5–6 of these gives you a fully branded widget. **Tier 2** (component overrides) provide fine-grained control over individual elements, defaulting to their Tier 1 counterparts when not set.
### Tier 1 — Semantic Tokens
| Token | Type | Description |
| -------------------- | ------ | -------------------------------------------------- |
| `fontFamily` | string | Font family for all text |
| `primaryColor` | color | Submit button, focus rings, spinner, toggle accent |
| `primaryColorHover` | color | Submit button hover state |
| `textColor` | color | Labels, titles, body text |
| `textColorSecondary` | color | Placeholders, muted text |
| `backgroundColor` | color | Widget container background |
| `inputBackground` | color | Input field background |
| `inputBorderColor` | color | Input field border |
| `inputBorderRadius` | size | Input field corner radius |
| `borderRadius` | size | Buttons, messages, containers |
| `errorColor` | color | Error borders and error text |
| `successColor` | color | Success borders and success text |
### Tier 2 — Component Overrides
These are optional. When not set, they inherit from Tier 1 tokens.
| Token | Defaults to | Targets |
| ------------------------ | ------------------ | ------------------------------ |
| `buttonBackground` | `primaryColor` | Submit button background |
| `buttonTextColor` | `#ffffff` | Submit button text |
| `buttonBorderRadius` | `borderRadius` | Submit button corners |
| `clearButtonBackground` | `#ffffff` | Clear/cancel button background |
| `clearButtonTextColor` | `textColor` | Clear/cancel button text |
| `clearButtonBorderColor` | `inputBorderColor` | Clear/cancel button border |
| `inputTextColor` | `textColor` | Text typed into inputs |
| `inputFocusBorderColor` | `primaryColor` | Input focus ring |
| `inputFontSize` | `15px` | Input text size |
| `labelColor` | `textColor` | Field labels |
| `labelFontSize` | `14px` | Field label size |
| `titleColor` | `textColor` | Widget title |
| `titleFontSize` | `18px` | Widget title size |
| `errorBackground` | computed | Error message background |
| `successBackground` | computed | Success message background |
### Reveal Card Tokens
These apply only to the [Reveal Card Widget](/v2/docs/card-widgets-reveal).
| Token | Targets |
| ------------------ | ---------------------------------------------------- |
| `cardBackground` | Card face background (supports CSS gradient strings) |
| `cardTextColor` | Card overlay text (PAN, name, expiry, CVV) |
| `cardBorderRadius` | Card corner radius |
***
## Examples
### Brand Colors Only
The simplest approach — set your primary color and font:
```html Activate Card theme={"system"}
```
```html Set PIN theme={"system"}
```
```html Reveal Card theme={"system"}
```
```html Reveal PIN theme={"system"}
```
### Full Customization
Override both semantic tokens and component-level tokens:
```html theme={"system"}
```
### Reveal Card with Custom Card Face
```html theme={"system"}
```
### Dark Theme with Overrides
Start from the built-in `night-shift` preset, then override individual tokens:
```html theme={"system"}
```
***
## Built-in Theme Presets
### `default`
Light background with blue accents. This is used when `theme` is not set or set to `"default"`.
### `night-shift`
Dark background with purple accents. Apply with `theme="night-shift"`:
```html theme={"system"}
```
***
## Notes
* **Unknown token keys are ignored.** If you pass a key that doesn't match a known token, it's silently dropped.
* **Invalid JSON falls back to defaults.** A malformed `styles` string logs a console warning and applies no overrides.
* **Token values are CSS values.** Colors accept any CSS color format (`#hex`, `rgb()`, `hsl()`, named colors). Sizes accept `px`, `rem`, `em`, etc. `cardBackground` supports gradient strings.
* **Tokens apply at widget initialization.** To change tokens after mount, update the `styles` attribute and the widget will re-apply.
# Charge Secured Statements
Source: https://docs.synctera.com/v2/docs/charge-secured-statements-guide
Synctera APIs provide all the raw data necessary to create a periodic account statement. The contents of a charge secured account statement may be governed by various regulations, therefore it is essential to add the necessary fields when creating a periodic statement. Please consult with your compliance officer further.
## Statement content
A `CHARGE_SECURED` account is directly linked to a separate deposit account. Information about *both* accounts will be included in a statement of type `CHARGE_SECURED`:
* All top-level information in the statement payload pertains to the `CHARGE_SECURED` account.
* Information in the `credit_summary` section of the statement payload also pertains to the `CHARGE_SECURED` account.
* Information in the `security_account_summary` section of the statement payload pertains to the linked deposit account.
* The transactions returned by [GET /v1/statements/\{statement\_id}/transactions](/v2/reference/getstatementtransactions) will also include transactions by the linked deposit account.
In order to produce regulatory-compliant, human-readable statements, this API provides the following information:
| Section | Field(s) | Description |
| --------------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| | `statement_type` | The type of the statement, i.e. `CHARGE_SECURED` |
| | `start_date` and `end_date` | The date interval covered by the statement, inclusive. |
| | `issue_date` | The date the statement was issued. |
| | `opening_balance` and `closing_balance` | The final posted balances recorded at the beginning of `start_date` and at the end of `end_date`. |
| | `disclosure` | A suggested disclosure statement to display. |
| | `total_transactions` | The number of transactions posted during this statement period. See [this section](/v2/docs/statements-guide#step-3-list-the-transactions-for-a-statement) for guidance on how to access the full list of transactions. |
| `account_summary` | - | Information about the account. |
| `account_summary` | `financial_institution` | Information about the financial institution managing the account. |
| `customer_service_details` | - | Contact information for use by the customer if they wish to dispute the information in this statement. |
| `primary_account_holder_personal` | - | When `account_summary.customer_type` is `PERSONAL` then `primary_account_holder_personal` contains information about the person acting as primary account holder. |
| `primary_account_holder_business` | - | When `account_summary.customer_type` is `BUSINESS` then `primary_account_holder_business` contains information about the company holding this account. |
| `joint_account_holders` | - | A list of all individuals designated as joint account holders for this account. |
| `authorized_signer` | - | A list of all individuals designated as authorized signers for this account. |
| `credit_summary` | `apr` | Describes the annual percentage rate in effect. |
| `credit_summary` | `balance_for_interest` | The balance used to calculate the interest accrued on the account. |
| `credit_summary` | `credit_limit` | The credit limit set on the account. For this account type, the credit limit is based on available funds in the security account, so the value depends on the state of the security account, and is not preset. |
| `credit_summary` | `interest` and `interest_ytd` | The total interest accrued at the end of this statement, both for this statement period and for the calendar year so far. |
| `credit_summary` | `fees` and `fees_ytd` | The total fees accrued at the end of this statement, both for this statement period and for the calendar year so far. |
| `credit_summary` | `minimum_payment_due` | The minimum payment amount expected by the payment due date. |
| `credit_summary` | `payment_due_date` | The date by which payment is expected. |
| `credit_summary` | `last_payment_date` | The date on which the last payment is received. |
| `credit_summary` | `payments_received` | The total payment amount received during the current billing priod. |
| `credit_summary` | `is_past_due` and `amount_past_due` | The indicator of an account which is past due and its past due amount. |
| `credit_summary` | `amount_over_limit` | The portion of the statement balance that exceeds the account's credit limit. |
| `security_account_summary` | `apy` | If the security account has an interest product associated with it, this field will describe the APY earned based on the interest payout in this period. |
| `security_account_summary` | `disclosure` | A suggested disclosure statement to display for the security account. |
| `security_account_summary` | `interest` and `interest_ytd` | The total interest accrued by the linked account at the end of this statement, both for this statement period and for the calendar year so far. |
| `security_account_summary` | `interest_previous_month` and `interest_previous_year` | The total interest accrued by the linked account at the end of this statement, both for the previous statement period and for the previous calendar year. |
| `security_account_summary` | `opening_balance` and `closing_balance` | The final posted balances for the linked account recorded at the beginning of `start_date` and at the end of `end_date`. |
# Checking and Saving accounts
Source: https://docs.synctera.com/v2/docs/checking-savings-accounts-guide
Please refer to our V2 Accounts guide [here](/v2/docs/create-accounts-guide).
# Checkings and Savings Statements
Source: https://docs.synctera.com/v2/docs/checking-savings-statements-guide
Synctera APIs provide all the raw data necessary to create a periodic account statement. The contents of a deposit account statement may be governed by various regulations, therefore it is essential to add the necessary fields when creating a periodic statement. Please consult with your compliance officer further.
## Statement content
In order to produce regulatory-compliant, human-readable statements, this API provides the following information:
| Section | Field(s) | Description |
| --------------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| | `statement_type` | The type of the statement, i.e. `DEPOSIT` |
| | `start_date` and `end_date` | The date interval covered by the statement, inclusive. |
| | `issue_date` | The date the statement was issued. |
| | `opening_balance` and `closing_balance` | The final posted balances recorded at the beginning of `start_date` and at the end of `end_date`. |
| | `disclosure` | A suggested disclosure statement to display. |
| | `total_transactions` | The number of transactions posted during this statement period. See [this section](/v2/docs/statements-guide#step-3-list-the-transactions-for-a-statement) for guidance on how to access the full list of transactions. |
| `account_summary` | - | Information about the account. |
| `account_summary` | `financial_institution` | Information about the financial institution managing the account. |
| `customer_service_details` | - | Contact information for use by the customer if they wish to dispute the information in this statement. |
| `primary_account_holder_personal` | - | When `account_summary.customer_type` is `PERSONAL` then `primary_account_holder_personal` contains information about the person acting as primary account holder. |
| `primary_account_holder_business` | - | When `account_summary.customer_type` is `BUSINESS` then `primary_account_holder_business` contains information about the company holding this account. |
| `joint_account_holders` | - | A list of all individuals designated as joint account holders for this account. |
| `authorized_signer` | - | A list of all individuals designated as authorized signers for this account. |
| `saving_summary` | `apy` | If the account has an interest product associated with it, this field will describe the APY earned based on the interest payout in this period. |
| `saving_summary` | `interest_earned` and `interest_earned_ytd` | If the account has an interest product associated with it, this field will describe the interest earned on during this billing period, and the sum of interest earned during the calendar year. |
| `saving_summary` | `interest_previous_month` and `interest_previous_year` | The total interest accrued by the linked account at the end of this statement, both for the previous statement period and for the previous calendar year. |
For more details about the schema returned by this API, please refer to the [Statements API specification](/v2/reference/getstatement).
Here is a sample statement produced using the information provided by this API.
# Create A Business Customer
Source: https://docs.synctera.com/v2/docs/create-a-business
On the Synctera platform, the business resource represents an organization that can be an account holder or the owner of another business, including sole proprietorships.
## Overview
A business is one of the core entities on Synctera: **beneficial owners**, **accounts**, **ACH**, and **cards** are all tied to the business resource. The object holds identification information (legal name, Employer Identification Number, address, phone number, email) and status attributes that track its lifecycle from prospect, to active business, to inactive former customer.
**Creating a business** involves more than a single call: you capture the people acting on its behalf, its ownership and control structure, the disclosures its agent acknowledges, and finally run *Know Your Business* (KYB) verification before the business can transact.
Key characteristics:
* **Composite** — a business is linked to people (beneficial owners, managers) and, potentially, to other owning businesses through relationships.
* **Dual-status** — an editable `status` (administrative state) and a read-only `verification_status` (KYB result) together gate what the business can do.
* **Verified** — most banking activity requires the business to pass KYB, which sets `verification_status` to `ACCEPTED`.
* **Reusable** — the returned `id` links the business to disclosures, accounts, cards, and watchlists.
## Prerequisites
This guide assumes you are familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
* [Create a Personal Customer](/v2/docs/create-a-personal-customer) — beneficial owners and managers are people
* [KYC/KYB Verification](/v2/docs/kyc-kyb-verification)
* [Record Disclosure Acceptance](/v2/docs/record-disclosure-acceptance)
The curl examples authenticate with an `apikey` environment variable. Some examples depend on identifiers generated by previous steps; these are shown as placeholders like `{BUSINESS_ID}`.
At a high level, creating a business follows these steps:
1. Create a person acting on behalf of the business.
2. Create the business.
3. Capture ownership and control structure (beneficial owners, managers, owning businesses).
4. Record disclosures, including beneficial ownership certification.
5. Activate the business.
6. Run KYB.
7. Use the business with other APIs.
## Creating and activating a business
Collect identity information for the person acting on behalf of the business — typically a beneficial owner or manager who is signing up for your product. If they are neither, review business documentation to verify their affiliation before creating an account for the business.
The snippet below creates a personal customer. Note `is_customer`: set it to `false` here, or to `true` if this person should also transact on the platform in their own right.
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/persons \
--data-binary '
{
"first_name": "John",
"middle_name": "James",
"last_name": "Doe",
"dob": "1981-09-14",
"email": "john@example.com",
"phone_number": "+16055512212",
"ssn": "456-78-9999",
"legal_address": {
"address_line_1": "123 Main St.",
"city": "San Francisco",
"state": "CA",
"postal_code": "94105",
"country_code": "US"
},
"is_customer": false,
"status": "ACTIVE"
}'
```
Use the `id` from the response to identify the person taking actions on behalf of the business.
Create the business with `PROSPECT` status to indicate it is not yet fully configured; missing attributes (e.g. addresses) can be added later. Set `is_customer` to `true` so the business can be an account holder rather than just an owner. Use [POST /v2/businesses](/v2/reference/createbusiness):
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/businesses \
--data-binary '
{
"status": "PROSPECT",
"is_customer": true,
"entity_name": "Acme Trap Company",
"trade_names": ["Acme", "Acme Corp", "CorporationID#77231"],
"formation_date": "2000-01-01",
"formation_state": "NY",
"structure": "CORPORATION",
"phone_number": "+12124567890",
"ein": "12-3456789",
"legal_address": {
"address_line_1": "50 Main St",
"city": "New York",
"state": "NY",
"postal_code": "12345",
"country_code": "US"
}
}'
```
The response includes the system-generated `id`, used for future `GET`, `PATCH`, and `DELETE` requests and to link the business to disclosures and accounts:
```json theme={"system"}
{
"id": "{BUSINESS_ID}",
"status": "PROSPECT",
"verification_status": "UNVERIFIED",
"is_customer": true,
"entity_name": "Acme Trap Company",
"trade_names": ["Acme", "Acme Corp", "CorporationID#77231"],
"formation_date": "2000-01-01",
"formation_state": "NY",
"structure": "CORPORATION",
"phone_number": "+12124567890",
"ein": "12-3456789",
"legal_address": {
"address_line_1": "50 Main St",
"city": "New York",
"state": "NY",
"postal_code": "12345",
"country_code": "US"
},
"creation_time": "2022-04-29T21:12:55.179008Z",
"last_updated_time": "2022-04-29T21:12:55.179008Z"
}
```
Capture the ownership structure of the business — required for KYB and compliance. Federal regulation requires financial institutions to obtain, verify, and record information about the beneficial owners of legal entities.
### What information do I have to provide?
Provide sufficient information to KYC — such as name, address, date of birth, and Social Security Number (or passport number or similar, for foreign persons) — for each individual who:
* Directly or indirectly owns **25% or more** of the equity interests of the legal entity (some banks require information on owners with a smaller share, depending on risk), **and**
* Has significant responsibility for managing the entity (e.g. CEO, CFO, COO, Managing Member, General Partner, President, Vice President, or Treasurer).
**Beneficial owners.** A business can be owned by people (beneficial owners) or by other businesses, which can in turn have their own owners. This example uses the simplest structure: a single beneficial owner. See [KYC/KYB Verification](/v2/docs/kyc-kyb-verification) for complex examples.
You can reuse the `id` of the person created in step 1 or create separate personal customers as beneficial owners:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/persons \
--data-binary '
{
"first_name": "Christopher",
"middle_name": "James",
"last_name": "Albertson",
"dob": "1985-06-14",
"email": "chris@example.com",
"phone_number": "+16045551212",
"ssn": "456-78-9999",
"legal_address": {
"address_line_1": "456 Main St.",
"city": "San Francisco",
"state": "CA",
"postal_code": "94105",
"country_code": "US"
},
"is_customer": false,
"status": "ACTIVE"
}'
```
Link the person to the business as a beneficial owner with [POST /v2/relationships](/v2/reference/createrelationship), using relationship type `BENEFICIAL_OWNER_OF`:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/relationships \
--data-binary '
{
"from_person_id": "{PERSON_ID}",
"relationship_type": "BENEFICIAL_OWNER_OF",
"to_business_id": "{BUSINESS_ID}",
"additional_data": {
"percent_ownership": 100
}
}'
```
**Business managers.** KYB also requires at least one managing person — someone who exercises significant control, such as an executive officer or board member. Link them with a `MANAGING_PERSON_OF` relationship:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/relationships \
--data-binary '
{
"from_person_id": "{PERSON_ID}",
"relationship_type": "MANAGING_PERSON_OF",
"to_business_id": "{BUSINESS_ID}",
"additional_data": {
"title": "OFFICER"
}
}'
```
**Owning businesses.** Collect information on any business that owns more than 25% of the business customer. Create a business resource for the owner, then link the two with an `OWNER_OF` relationship:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/relationships \
--data-binary '
{
"from_business_id": "{OWNING_BUSINESS_ID}",
"relationship_type": "OWNER_OF",
"to_business_id": "{OWNED_BUSINESS_ID}",
"additional_data": {
"percent_ownership": 25.1
}
}'
```
Once all ownership information is entered, have the agent certify that it is complete and correct. Display the following text and record an `OWNER_CERTIFICATION` [disclosure](#step-4-record-disclosures) on save:
> I certify to the best of my knowledge that the information entered is complete and correct.
Onboarding a business involves disclosing information such as terms of service. Start by tracking the beneficial ownership certification.
**Beneficial ownership disclosure.** You need the `id` of the person entering beneficial-owner information (from step 1) and the business `id`. That person should be related to the business as a `BENEFICIAL_OWNER_OF` or `MANAGING_PERSON_OF`; if not, collect business formation documentation and verify their relationship first. If they are not yet a customer, create a person to represent them.
Create the record with [POST /v2/disclosures](/v2/reference/createdisclosure), using the `OWNER_CERTIFICATION` type. `acknowledging_person_id` and `business_id` are required:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/disclosures \
--data-binary '
{
"acknowledging_person_id": "{PERSON_ID}",
"business_id": "{BUSINESS_ID}",
"type": "OWNER_CERTIFICATION",
"version": "1.0",
"event_type": "ACKNOWLEDGED",
"disclosure_date": "2022-04-17T02:04:34Z"
}'
```
The response is a disclosure object:
```json theme={"system"}
{
"id": "{DISCLOSURE_ID}",
"acknowledging_person_id": "{PERSON_ID}",
"business_id": "{BUSINESS_ID}",
"type": "OWNER_CERTIFICATION",
"version": "1.0",
"event_type": "ACKNOWLEDGED",
"disclosure_date": "2022-04-17T00:00:00Z",
"creation_time": "2022-04-29T21:14:24.953307Z",
"last_updated_time": "2022-04-29T21:14:24.953307Z"
}
```
**Other disclosures.** Record other types such as `REG_E` or `E_SIGN` with the same [endpoint](/v2/reference/createdisclosure). Work with your Synctera partner to determine which disclosures your business needs:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/disclosures \
--data-binary '
{
"business_id": "{BUSINESS_ID}",
"acknowledging_person_id": "{PERSON_ID}",
"type": "REG_E",
"version": "1.0",
"event_type": "ACKNOWLEDGED",
"disclosure_date": "2022-04-17T02:04:34Z"
}'
```
Once you have collected enough information to run KYB and the entity is ready to do business on your platform, move it to `ACTIVE` with [PATCH /v2/businesses/\{BUSINESS\_ID}](/v2/reference/updatebusiness):
```shell theme={"system"}
curl \
-X PATCH \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/businesses/{BUSINESS_ID} \
--data-binary '
{
"status": "ACTIVE"
}'
```
An `ACTIVE` business whose `verification_status` is still `UNVERIFIED` cannot perform most banking activities. Verify its identity with a *Know Your Business* (KYB) check using [POST /v2/verifications/verify](/v2/reference/verify):
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/verifications/verify \
--data-binary '
{
"customer_consent": true,
"customer_ip_address": "{CUSTOMER_IP_ADDRESS}",
"business_id": "{BUSINESS_ID}"
}'
```
This returns a list of all the verifications that were run and a result for each. See the [KYC/KYB Verification](/v2/docs/kyc-kyb-verification) guide for the full range of verification and risk checks.
If the verifications pass, the business's `verification_status` becomes `ACCEPTED`. Confirm with [GET /v2/businesses/\{BUSINESS\_ID}](/v2/reference/getbusiness):
```shell theme={"system"}
curl \
-X GET \
-H "Authorization: Bearer $apikey" \
https://api.synctera.com/v2/businesses/{BUSINESS_ID}
```
```json theme={"system"}
{
"id": "{BUSINESS_ID}",
"status": "ACTIVE",
"verification_status": "ACCEPTED",
"verification_last_run": "2022-04-29T21:19:09.432601Z",
"is_customer": true,
"entity_name": "Acme Trap Company",
"trade_names": ["Acme", "Acme Corp", "CorporationID#77231"],
"formation_date": "2000-01-01",
"formation_state": "NY",
"structure": "CORPORATION",
"phone_number": "+12124567890",
"ein": "12-3456789",
"legal_address": {
"address_line_1": "50 Main St",
"city": "New York",
"state": "NY",
"postal_code": "12345",
"country_code": "US"
},
"creation_time": "2022-04-29T21:12:55.179008Z",
"last_updated_time": "2022-04-29T21:19:09.521182Z"
}
```
An active, verified business can be used across the platform:
* The [Accounts](/v2/docs/create-accounts-guide) API creates and manages accounts with the business as an account holder.
* The [Cards](/v2/docs/personal-cards) API creates and manages credit or debit cards linked to the business.
* The **Relationships** API links the business to another business as an owner.
* The **Watchlists** API subscribes the business to watchlist monitoring so you can check for its presence on security risk watchlists.
## Business status attributes
A business carries two independent status attributes, and both gate money movement, card issuance, and other operations:
* `status` — an **editable** attribute representing the administrative state of the business.
* `verification_status` — a **read-only** attribute set by the platform to reflect the result of KYB and other verification checks.
### Business status
The `status` attribute is controlled by your API client. It is up to you to decide what you consider an `ACTIVE`, `FROZEN`, or `INACTIVE` business. All states other than `ACTIVE` are restricted and do not allow most banking operations.
| Status | Description |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `ACTIVE` | An active business. |
| `CANCELLED` | The business filed a cancellation, or failed to file its periodic report after notice of forfeiture of its rights to do business. |
| `CONVERTED` | The business type changed to another type within the same jurisdiction. |
| `DISSOLVED` | The business filed articles of dissolution or a certificate of termination. |
| `FROZEN` | The business's actions are blocked for security, legal, or other reasons. |
| `INACTIVE` | No longer active, e.g. a former customer. |
| `MERGED` | The business ceased to exist by merging into another entity. |
| `PROSPECT` | A potential business customer, used for information-gathering and disclosures. |
| `SUSPENDED` | The business lost the right to operate in its registered jurisdiction. |
There are no restrictions on state transitions, but certain fields (e.g. `entity_name`) are required by all states except `PROSPECT`. Typical transitions:
```mermaid theme={"system"}
%%{init: {"fontFamily": "sans-serif"}}%%
stateDiagram-v2
[*] --> PROSPECT: Prospect identified
PROSPECT --> ACTIVE: Prospect qualified
ACTIVE --> INACTIVE: Closed accounts
ACTIVE --> FROZEN: Anomalous activity
FROZEN --> ACTIVE: Anomalies resolved
ACTIVE --> CANCELLED
ACTIVE --> CONVERTED
ACTIVE --> DISSOLVED
ACTIVE --> MERGED
ACTIVE --> SUSPENDED
```
### Verification status
To initiate transactions, an active business must also pass verification (identity and watchlist checks). All states other than `ACCEPTED` are restricted and do not allow most banking operations.
| Verification status | Description |
| ------------------- | ----------------------------------------------------------- |
| `ACCEPTED` | The business is successfully verified. |
| `PENDING` | Verification is in progress. |
| `PROVISIONAL` | Partially verified or verified with restrictions. |
| `REJECTED` | The business was rejected. |
| `REVIEW` | Verification ran and identified issues that require review. |
| `UNVERIFIED` | Verification has not been completed. |
See the [KYC/KYB Verification](/v2/docs/kyc-kyb-verification) guide for how verifications are performed and what the results mean.
## Additional business documentation
To mitigate fraud and money laundering, we recommend gathering the following documentation and attaching it to the business with the [Documents API](/v2/reference/createdocument):
* **Legal entity organization documentation** — articles of incorporation, partnership agreement, certificate of organization, operating agreement, etc.
* **Money Services Business (MSB) status** — whether the entity is a registered MSB and, if so, its FinCEN registration.
* **Ongoing re-KYB** (e.g. annually or bi-annually) — required for certain higher-risk accounts subject to the Sponsor Bank's policies, or whenever unusual activity occurs. Higher-risk examples include cash-intensive businesses, non-US businesses, crypto businesses, MSBs, and marijuana-related businesses.
## Best practices
A business cannot perform most banking operations until both `status` is `ACTIVE` **and** `verification_status` is `ACCEPTED`. Capture the full ownership structure before running KYB — incomplete beneficial-owner data is a common cause of verification failures.
* **Start as a `PROSPECT`** — create the business early and enrich it as information arrives, activating only when it is ready to transact.
* **Model the full ownership tree** — link every beneficial owner (≥25%), at least one managing person, and any owning businesses before KYB.
* **Record the ownership certification** — capture the `OWNER_CERTIFICATION` disclosure from the agent who entered the data.
* **Never rely on `status` alone** — an `ACTIVE` business may still be `UNVERIFIED`; gate sensitive actions on both attributes.
* **Plan for re-KYB** — schedule periodic re-verification for higher-risk businesses per your Sponsor Bank's policies.
## Related guides
Onboard the people who act as beneficial owners and managers.
Understand KYB checks and complex ownership verification.
Record ownership certification and other required disclosures.
Submit extra information for high-risk businesses and CRR responses.
Open accounts with the business as an account holder.
## API reference
* [Create a business](/v2/reference/createbusiness)
* [Get a business](/v2/reference/getbusiness)
* [Update a business](/v2/reference/updatebusiness)
* [Create a relationship](/v2/reference/createrelationship)
* [Create a disclosure](/v2/reference/createdisclosure)
* [Run a verification](/v2/reference/verify)
* [Create a document](/v2/reference/createdocument)
# Create A Personal Customer
Source: https://docs.synctera.com/v2/docs/create-a-personal-customer
A person represents a natural person on the Synctera platform — an account holder, a beneficial owner of a business, or an authorized signer. A person can hold several of these roles at once.
## Overview
A person is one of the core entities in the Synctera platform: **accounts**, **ACH**, and **cards** are all tied to the person resource. The object holds personal identification information (name, Social Security Number, address, phone number, email) and status attributes that track its lifecycle from prospect, to active customer, to inactive former customer.
**Creating a person** is the first step in onboarding a customer. You build the record up over time — starting from a partial `PROSPECT`, gathering the rest of the identification, running identity verification, and finally linking the person to accounts, cards, and other resources.
Key characteristics:
* **Progressive** — create a person with only a subset of identification as a `PROSPECT`, then fill in the rest and activate it.
* **Dual-status** — an editable `status` (administrative state) and a read-only `verification_status` (identity-verification result) together gate what the person can do.
* **Verified** — most banking activity requires the person to pass a *Know Your Customer* (KYC) check, which sets `verification_status` to `ACCEPTED`.
* **Reusable** — the returned `id` links the person to disclosures, accounts, cards, relationships, and watchlists.
## Prerequisites
This guide assumes you are familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
The curl examples authenticate with an `apikey` environment variable. Some examples depend on identifiers generated by previous steps; these are shown as placeholders like `{PERSON_ID}`.
## The person object
A person contains the following key fields:
| Field | Description |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | Unique identifier (read-only, assigned on creation). Used for `GET`, `PATCH`, and `DELETE`, and to link the person to other resources. |
| `status` | **Editable.** Administrative state of the person (`PROSPECT`, `ACTIVE`, `FROZEN`, etc.). See [Person status](#person-status). |
| `verification_status` | **Read-only.** Result of the verification process (`UNVERIFIED`, `ACCEPTED`, etc.). See [Verification status](#verification-status). |
| `is_customer` | Whether the person is a customer of the platform. |
| `first_name` / `last_name` | Legal name. Required for all statuses except `PROSPECT`. |
| `chosen_name` | Preferred name. Searching by `first_name` also matches this field. |
| `dob` | Date of birth. |
| `ssn` | Social Security Number. Stored in a vault; returned masked or omitted. See [Handling sensitive attributes](#handling-sensitive-attributes). |
| `email` / `phone_number` | Contact details. |
| `legal_address` / `shipping_address` | Structured address objects. |
```json theme={"system"}
{
"id": "5f4ff599-7c29-4f69-a3d9-e103e151afbd",
"status": "ACTIVE",
"verification_status": "ACCEPTED",
"first_name": "Anne",
"chosen_name": "Annie",
"last_name": "Baker",
"dob": "2000-01-01",
"ssn": "6789",
"email": "anne@example.com",
"phone_number": "+12124567890",
"legal_address": {
"address_line_1": "50 Main St",
"city": "New York",
"state": "NY",
"postal_code": "12345",
"country_code": "US"
},
"shipping_address": {
"address_line_1": "99 Elm St",
"city": "San Antonio",
"state": "TX",
"postal_code": "77777",
"country_code": "US"
},
"creation_time": "2022-04-13T01:52:40.690387Z",
"last_updated_time": "2022-04-13T18:09:12.881517Z",
"verification_last_run": "2022-04-13T18:09:12.880623Z"
}
```
See the [API reference](/v2/reference/createperson) for the full request and response schemas.
## Creating and activating a person
Use [POST /v2/persons](/v2/reference/createperson). If you have only a subset of the identification (e.g. an email but not a name), create the person with `PROSPECT` status:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/persons \
--data-binary '
{
"status": "PROSPECT",
"email": "anne@example.com",
"is_customer": true
}'
```
The response includes the system-generated `id`, which links the person to disclosures, accounts, and other resources:
```json theme={"system"}
{
"id": "{PERSON_ID}",
"status": "PROSPECT",
"verification_status": "UNVERIFIED",
"email": "anne@example.com",
"creation_time": "2022-04-13T01:52:40.690387Z",
"last_updated_time": "2022-04-13T01:52:40.690387Z"
}
```
Onboarding typically involves disclosing information such as terms of service. See [Customer Disclosures](/v2/docs/record-disclosure-acceptance) for details.
To record that the person acknowledged a disclosure, use [POST /v2/disclosures](/v2/reference/createdisclosure) with the person's ID and the disclosure they acknowledged:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/disclosures \
--data-binary '
{
"person_id": "{PERSON_ID}",
"type": "REG_DD",
"version": "1.0",
"event_type": "ACKNOWLEDGED",
"disclosure_date": "2022-04-17T02:04:34Z"
}'
```
The response is a disclosure object:
```json theme={"system"}
{
"id": "{DISCLOSURE_ID}",
"person_id": "{PERSON_ID}",
"type": "REG_DD",
"version": "1.0",
"event_type": "ACKNOWLEDGED",
"disclosure_date": "2022-04-17T00:00:00Z",
"creation_time": "2022-04-13T02:17:34.895861Z",
"last_updated_time": "2022-04-13T02:17:34.895861Z"
}
```
Once you have gathered the rest of the identification, use [PATCH /v2/persons/\{PERSON\_ID}](/v2/reference/updateperson) to add the remaining data and move the person to `ACTIVE` status:
```shell theme={"system"}
curl \
-X PATCH \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/persons/{PERSON_ID} \
--data-binary '
{
"status": "ACTIVE",
"first_name": "Anne",
"last_name": "Baker",
"chosen_name": "Annie",
"dob": "2000-01-01",
"phone_number": "+12124567890",
"ssn": "123-45-6789",
"legal_address": {
"address_line_1": "50 Main St",
"city": "New York",
"state": "NY",
"postal_code": "12345",
"country_code": "US"
},
"shipping_address": {
"address_line_1": "99 Elm St",
"city": "San Antonio",
"state": "TX",
"postal_code": "77777",
"country_code": "US"
}
}'
```
When listing persons, searching by `first_name` also searches the `chosen_name` field.
An `ACTIVE` person whose `verification_status` is still `UNVERIFIED` cannot perform most banking activities. Verify their identity with a *Know Your Customer* (KYC) check using [POST /v2/verifications/verify](/v2/reference/verify):
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/verifications/verify \
--data-binary '
{
"customer_consent": true,
"customer_ip_address": "{IP_ADDRESS}",
"person_id": "{PERSON_ID}"
}'
```
This returns a list of all the verifications that were run and a result for each. See the [KYC/KYB Verification](/v2/docs/kyc-kyb-verification) guide for the full range of verification and risk checks.
If the verifications pass, the person's `verification_status` becomes `ACCEPTED`. Confirm with [GET /v2/persons/\{PERSON\_ID}](/v2/reference/getperson):
```shell theme={"system"}
curl \
-X GET \
-H "Authorization: Bearer $apikey" \
https://api.synctera.com/v2/persons/{PERSON_ID}
```
```json theme={"system"}
{
"id": "{PERSON_ID}",
"status": "ACTIVE",
"verification_status": "ACCEPTED",
"verification_last_run": "2022-04-13T18:09:12.880623Z",
"first_name": "Anne",
"chosen_name": "Annie",
"last_name": "Baker",
"dob": "2000-01-01",
"ssn": "6789",
"email": "anne@example.com",
"phone_number": "+12124567890",
"legal_address": {
"address_line_1": "50 Main St",
"city": "New York",
"state": "NY",
"postal_code": "12345",
"country_code": "US"
},
"shipping_address": {
"address_line_1": "99 Elm St",
"city": "San Antonio",
"state": "TX",
"postal_code": "77777",
"country_code": "US"
},
"creation_time": "2022-04-13T01:52:40.690387Z",
"last_updated_time": "2022-04-13T18:09:12.881517Z"
}
```
An active, verified person can be used across the platform:
* The [Accounts](/v2/docs/create-accounts-guide) API creates and manages accounts with the person as an account holder or authorized signer.
* The [Cards](/v2/docs/personal-cards) API creates and manages credit or debit cards linked to the person.
* The **Relationships** API links the person to a business as a beneficial owner or officer.
* The **Watchlists** API subscribes the person to watchlist monitoring so you can check for their presence on security risk watchlists.
## Person status attributes
A person carries two independent status attributes, and both gate money movement, card issuance, and other operations:
* `status` — an **editable** attribute representing the administrative state of the person. Use it to indicate whether you consider the person active, dormant, etc.
* `verification_status` — a **read-only** attribute set by the platform to reflect the result of the verification process (e.g. KYC checks).
### Person status
The `status` attribute is controlled by your API client. It is up to you to decide what you consider an `ACTIVE`, `FROZEN`, or `INACTIVE` customer. All states other than `ACTIVE` are restricted and do not allow most banking operations.
| Status | Description |
| ---------- | ----------------------------------------------------------------------- |
| `ACTIVE` | An active customer. |
| `DECEASED` | The person is deceased. |
| `DENIED` | The customer was turned down. |
| `DORMANT` | Inactive due to an extended period without any transactions. |
| `ESCHEAT` | The person's assets are abandoned and are property of the state. |
| `FROZEN` | The person's actions are blocked for security, legal, or other reasons. |
| `INACTIVE` | No longer active, e.g. a former customer. |
| `PROSPECT` | A potential customer, used for information-gathering and disclosures. |
There are no restrictions on state transitions, but certain fields (e.g. `first_name`, `last_name`) are required by all states except `PROSPECT`. Typical transitions:
```mermaid theme={"system"}
stateDiagram-v2
[*] --> PROSPECT: Prospect identified
PROSPECT --> ACTIVE: Prospect qualified
ACTIVE --> INACTIVE: Closed accounts
ACTIVE --> FROZEN: Anomalous activity
FROZEN --> ACTIVE: Anomalies resolved
ACTIVE --> DORMANT: No activity
DORMANT --> ACTIVE
ACTIVE --> DECEASED
ACTIVE --> DENIED: Turned down
```
### Verification status
To initiate transactions, an active person must also pass verification (KYC, watchlists, and other checks). All states other than `ACCEPTED` are restricted and do not allow most banking operations.
| Verification status | Description |
| ------------------- | ----------------------------------------------------------- |
| `ACCEPTED` | The person is successfully verified. |
| `PENDING` | Verification is in progress. |
| `PROVISIONAL` | Partially verified or verified with restrictions. |
| `REJECTED` | The person was rejected. |
| `REVIEW` | Verification ran and identified issues that require review. |
| `UNVERIFIED` | Verification has not been completed. |
See the [KYC/KYB Verification](/v2/docs/kyc-kyb-verification) guide for how verifications are performed and what the results mean.
## Handling sensitive attributes
Some information you collect is personally identifiable information (PII) that can identify an individual directly or indirectly. The Synctera platform encrypts all data at rest.
Some PII, such as an SSN, is especially confidential and requires further safeguards. Synctera stores those values in a vault to control access. When an endpoint later retrieves a resource containing such protected attributes, they are either omitted from the response or returned masked.
## Best practices
A person cannot perform most banking operations until both `status` is `ACTIVE` **and** `verification_status` is `ACCEPTED`. Check both before attempting money movement or card issuance.
* **Start as a `PROSPECT`** — capture partial identification early, then enrich the record and activate it once you have the full data set.
* **Record disclosures before activation** — keep a clear audit trail of what the customer acknowledged and when.
* **Capture consent for KYC** — always send `customer_consent` and `customer_ip_address` when running verification.
* **Never rely on `status` alone** — an `ACTIVE` person may still be `UNVERIFIED`; gate sensitive actions on both attributes.
* **Treat PII carefully** — expect vaulted fields like `ssn` to be returned masked, and never log full values.
## Related guides
Record the disclosures a customer acknowledges during onboarding.
Understand the verification checks that set `verification_status`.
Open accounts with the person as an account holder or authorized signer.
Onboard an organization and link people to it as owners or managers.
Submit extra information for high-risk customers and CRR responses.
## API reference
* [Create a person](/v2/reference/createperson)
* [Get a person](/v2/reference/getperson)
* [List persons](/v2/reference/listpersons)
* [Update a person](/v2/reference/updateperson)
* [Run a verification](/v2/reference/verify)
* [Create a disclosure](/v2/reference/createdisclosure)
# Accounts
Source: https://docs.synctera.com/v2/docs/create-accounts-guide
In the Synctera platform, an account represents a bank account of your business or personal customer. This guide explains how to create and use accounts.
## Types of Accounts
Synctera supports multiple account types.
* `SAVING` and `CHECKING` are Demand Deposit Accounts (DDA) and represent funds accrued by the account holder.
* A `LINE_OF_CREDIT` account represents unsecured line of credit extended to the account holder.
* A `CHARGE_SECURED` account represents secured non-revolving credit extended to the account holder, secured by a linked DDA.
* A `CHARGE_UNSECURED `account represents an unsecured non-revolving credit extended to the account holder.
* A `CREDIT_CARD` account represents a revolving line of credit for use with a credit card
* A `PREPAID` account represents a stored-value card that is attached to a prepaid card. Prepaid accounts can be created for either reloadable or non-reloadable card types.
In addition to the various types of customer accounts, there are also:
* `GENERAL_LEDGER` accounts - Internal GL accounts (settlement, reserve, P\&L, etc). These are created and managed by Synctera or the bank only and are not created directly by integrators in production.
Other Important Concepts:
* Internal Accounts - a set of pre-defined general ledger accounts linked to the FinTech, not individual customers. See the [Internal Account](https://docs.synctera.com/v2/reference/listinternalaccounts) API.
* External Accounts: a link to an account outside the Synctera platform. See the [External Accounts](https://docs.synctera.com/v2/docs/external-accounts-guide) guide.
### Prerequisites
This guide assumes that you have:
1. Created a [personal customer](/v2/docs/create-a-personal-customer) or [business customer](/v2/docs/create-a-business)
2. Created and recorded the required [disclosure](/v2/docs/record-disclosure-acceptance)s for the end customer, such as account agreements
3. [Verified a customer](/v2/docs/kyc-kyb-verification) by running KYC/KYB and obtaining the proper verification status
In order to open an account and participate in money movement, all account holders, joint holders, and authorized signers are required to [undergo KYC or KYB verification](/v2/docs/kyc-kyb-verification) and have a `verification_status` of `ACCEPTED`.
You should also have the following environment variables set, as described in **Need to Know**:
* `baseurl` – points to the correct environment (sandbox or production).
* `apikey` – API key for that environment.
See:
* [Need to Know – Environments](https://docs.synctera.com/v2/reference/need-to-know#environments)
* [Need to Know – Authentication](https://docs.synctera.com/v2/reference/need-to-know#authentication)
### Customer Device Fingerprinting
All Synctera API endpoints support **customer device fingerprinting** via the Customer-Device-Info HTTP header. For any request made **on behalf of an end-customer** (for example, opening an account from your mobile app), you should include this header with device telemetry:
```json theme={"system"}
{
"customer_id": "123e4567-e89b-12d3-a456-426614174000",
"ip_address": "203.0.113.195",
"device_type": "iOS",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1"
}
```
For more details, see [Need to Know – Customer Device Fingerprinting](https://docs.synctera.com/v2/reference/need-to-know#customer-device-fingerprinting).
## Creating an Account
This section will walk you through the creation of a basic account and getting it ready to use with other features such Cards and ACH. This assumes you have set up `baseurl` and `apikey` environment variables. See [Base URL](/v2/reference/need-to-know#environments) and [Authentication](/v2/reference/need-to-know#authentication) for instructions.
Call [POST /v2/accounts](/v2/reference/createaccount) to create the account, linking to the existing customer.
```shell Shell theme={"system"}
curl \
-X POST \
$baseurl/v0/accounts \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
--data-binary '
{
"account_type": "SAVING",
"relationships": [
{
"relationship_type": "PRIMARY_ACCOUNT_HOLDER",
"customer_id": "{CUSTOMER_ID}"
}
]
}'
```
On success, the API will return a response with the created account, e.g.:
```json JSON theme={"system"}
{
"access_status": "ACTIVE",
"access_status_last_updated_time": "2022-10-06T14:03:54.962704Z",
"account_number": "790954299503",
"account_number_masked": "790*****9503",
"account_type": "SAVING",
"balances": [
{
"balance": 0,
"type": "ACCOUNT_BALANCE"
},
{
"balance": 0,
"type": "AVAILABLE_BALANCE"
}
],
"bank_routing": "112233445",
"creation_time": "2022-10-06T14:03:54.963162Z",
"currency": "USD",
"customer_ids": [
"110acc45-0b53-4d21-b624-29f88879c6a0"
],
"customer_type": "PERSONAL",
"id": "ce439ca4-8eb6-45e6-955c-46441af33770",
"is_account_pool": false,
"is_ach_enabled": false,
"is_card_enabled": false,
"is_p2p_enabled": false,
"is_wire_enabled": false,
"last_updated_time": "2022-10-06T14:03:54.963162Z",
"status": "ACTIVE_OR_DISBURSED"
}
```
Once created, you can use this account with other APIs, e.g.
* Cards - create a card on the account
* Transaction rails your program supports (e.g. ACH, SyncteraPay, Internal Transfers, AFT/OCT, Wires, mRDC, etc)
### Account Relationships
A customer can be related to an account in different ways:
| relationship\_type | Description |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| PRIMARY\_ACCOUNT\_HOLDER | Required, must have one primary owner on the account. Requires an `ACCEPTED` verification status. |
| JOINT\_ACCOUNT\_HOLDER | Joint owner with the same permissions as the Primary Account Holder. Requires an `ACCEPTED` verification status. |
| AUTHORIZED\_SIGNER | The authorized signer is a person that can transact on the account but is not an account owner. Requires an `ACCEPTED` verification status. |
| AUTHORIZED\_USER | The authorized user is a person that can only transact on the account. This person **does not** require an `ACCEPTED` verification status but an OFAC check will be completed by the platform using the customer's name and address. This means that you will not need to collect information typically used in KYC such as SSN, SIN, date of birth, but should collect name and address to minimize OFAC false positives. |
You can create relationships inline when creating an account, or you can manage them separately using the [/v2/accounts/relationships](/v2/reference/createaccountrelationship) endpoints.
The relationships should use the `customer_id` attribute if the account holder is a personal customer, and `business_id` if a business customer.
Example:
```text theme={"system"}
curl \
-X POST \
"$baseurl/v2/accounts/{ACCOUNT_ID}/relationships" \
-H "Authorization: Bearer $apikey" \
-H "Content-Type: application/json" \
--data-binary '{
"relationship_type": "JOINT_ACCOUNT_HOLDER",
"customer_id": "{JOINT_CUSTOMER_ID}",
"ownership_percentage": 50.0
}'
```
### Status
The account object contains two status attributes:
* `access_status`: can be set to `FROZEN` to temporarily freeze an account. This is an operational access state.
* `status` represents the lifecycle of the account. A FinTech can progress the account from `ACTIVE_OR_DISBURSED` to `IN_CLOSING` if the balance of the account is zero. The Synctera platform also updates this attribute, e.g. setting it to `RESTRICTED` when the account holder has not been verified. Note that authorized signers do not need to be verified.
* An account with a `status` of `IN_CLOSING` is only allowed to transition to `CLOSED` . Accounts are transitioned to `CLOSED` by the system after account closure is initiated and all system checks pass (see below section) Account status update to any other status from `IN_CLOSING` is not allowed.
To participate in money movement transactions, issue cards, etc, the account must have an `access_status` of `ACTIVE` and a `status` of `ACTIVE_OR_DISBURSED`.
Account Statuses
| **status** | **Description** | **Transactable** |
| :------------------------ | :------------------------------------------------------------------------------- | :--------------- |
| APPLICATION\_SUBMITTED | Deprecated application state. | N |
| ACTIVE\_OR\_DISBURSED | Account is open and can transact. | Y |
| ACCOUNT\_NEVER\_ACTIVE | Account was never activated. | N |
| ACCOUNT\_NOT\_DESIRED | Credit account created but customer did not accept. | N |
| ACTIVATED\_NOT\_DISBURSED | Credit account activated but funds not yet disbursed/used. | N |
| FAILED\_KYC | We were unable to verify the account holder (failed KYC). | N |
| RESTRICTED | Account holder has not successfully passed KYC; account is restricted. | N |
| DELINQUENT | Credit account is delinquent (past due on required payments). | N |
| SUSPENDED | Suspected fraudulent activity; blocked from further transactions. | N |
| IN\_CLOSING | Account is in the process of being closed. Only allowed transition is to CLOSED. | N |
| CLOSED | Account closed (balance must be zero). Cannot transact. | N |
| CHARGED\_OFF | Charged‑off account (closed with loss). | N |
| AWAITING\_FIXING | Account is awaiting remediation. | N |
### Balance Floor and Ceiling
The optional `balance_floor` and `balance_ceiling` attributes control what happens when the account balance goes below or above a set level. This can be used to implement sweep accounts.
* **balance\_floor** – defines a minimum balance and a linked overdraft account (if required).
* **balance\_ceiling** – defines a maximum balance and an overflow account.
Please see the [Balance Floor and Ceiling](https://docs.synctera.com/v2/docs/balance-floor-ceiling) guide for more details.
## Balances
Accounts have two types of balances: `ACCOUNT_BALANCE` and `AVAILABLE_BALANCE`.
For `SAVING` and `CHECKING` accounts:
* `ACCOUNT_BALANCE`: the amount of money in the account. Equal to the sum of credits minus debits for all posted transactions.
* `AVAILABLE_BALANCE`: the account balance minus any pending debits.
For `LINE_OF_CREDIT` accounts:
* `ACCOUNT_BALANCE`: the amount of credit currently in use. Equal to the sum of debits minus credits for all posted transactions.
* `AVAILABLE_BALANCE`: the amount of credit available. Equal to the credit limit minus `ACCOUNT_BALANCE` minus any pending debits.
You can read balances via the **account GET** endpoints or via transaction queries. See the [Accounts API Reference](https://docs.synctera.com/v2/reference/getaccount).
## Account Products and Templates
Synctera supports the concepts of Account Products and Account Templates to so you don't need to specify all the details when creating similar accounts.
### Account Products
An account product is a set of attributes that define how interest is calculated for accounts (and, in v2 (alpha), fees via dedicated fee products). The account product resource acts as a profile that can apply to multiple accounts. Changes to the account product affects all accounts that reference it. Only interest-bearing accounts need to reference an account product. Details about interest calculation can be found here in the [Interest Guide](/v2/docs/interest-guide).
Using an account product is optional. If no account product is specified when creating the account, no interest will be calculated.
It is not currently possible to change the account product on an account.
To create an interest product\_type, use [POST /v0/accounts/products](/v2/reference/createaccountresourceproduct) with `product_type = "INTEREST"`.
Example:
```shell Shell theme={"system"}
curl \
-X POST \
$baseurl/v0/accounts/products \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
--data-binary '
{
"product_type": "INTEREST",
"description": "Sample interest request body",
"calculation_method": "COMPOUNDED_MONTHLY",
"accrual_payout_schedule": "MONTHLY",
"rates": [
{
"valid_from": "2021-06-15",
"rate": 100,
"accrual_period": "DAILY"
},
{
"valid_from": "2021-06-01",
"valid_to": "2021-06-15",
"rate": 100,
"accrual_period": "DAILY"
}
]
}'
```
This will return a response with the created account product, e.g.:
```json JSON theme={"system"}
{
"accrual_payout_schedule": "MONTHLY",
"calculation_method": "COMPOUNDED_MONTHLY",
"description": "Sample interest request body",
"id": "52c80838-90b3-4afb-b3ff-98a97b5df96b",
"product_type": "INTEREST",
"rates": [
{
"accrual_period": "DAILY",
"rate": 100,
"valid_from": "2021-06-01",
"valid_to": "2021-06-15"
},
{
"accrual_period": "DAILY",
"rate": 100,
"valid_from": "2021-06-15"
}
]
}
```
Note the returned `id` attribute. This is used for when creating an account template and referenced as `interest_product_id` on templates and accounts.
### Account Templates
Account templates contain predefined values for creating an account. When creating an account, the Accounts API copies all the template values to the account resource. Once you create an account, the API no longer references the template used to create it, and updates won’t affect existing accounts. To create an account template, call [POST /v2accounts/templates](/v2/reference/createaccounttemplate).
All account creation uses an account template. As a convenience, the `account_template_id` does not need to be specified if there is a single account template that matches the requested account's type. As part of initial the FinTech setup, a simple account template for each account type is automatically created.
You can override template values in the `POST /v0/accounts` request when creating an account. However, the account type in the template and the `POST` request must agree.
To create an account template, specify the type of account (`CHECKING`, `SAVING`, `LINE_OF_CREDIT` or `CHARGE_SECURED`), and the attributes for that type. The `is_enabled` attribute must be set to `true` to use this template when creating an account. This example will use a `SAVING` account.
Key Notes:
* **Rails flags**: `is_ach_enabled`, `is_card_enabled`, `is_eft_ca_enabled`, `is_external_card_enabled`, `is_p2p_enabled`, `is_synctera_pay_enabled`, `is_wire_enabled` determine which payment rails are allowed and are enforced both via APIs and console operations.
* `interest_product_id` is optional. If omitted, no interest is calculated for accounts using this template.
* `spend_control_id` on templates/accounts should be used for spending limits; spending\_limits is deprecated. See the [Spend Controls guide](https://docs.synctera.com/v2/docs/spend-controls-guide).
* `balance_floor` and `balance_ceiling` are optional attributes that can also be set on the account template level.
Example Template Creation:
```shell Shell theme={"system"}
curl \
-X POST \
$baseurl/v0/accounts/templates \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
--data-binary '
{
"name": "sample template",
"description": "Sample account template",
"is_enabled": true,
"template": {
"account_type": "SAVING",
"bank_country": "US",
"currency": "USD",
"is_ach_enabled": true,
"is_card_enabled": true,
"is_p2p_enabled": true,
"interest_product_id": "{ACCOUNT_PRODUCT_ID}",
"overdraft_limit": 0
}
}'
```
This will return a response with the created account template, e.g.:
```json JSON theme={"system"}
{
"description": "Sample account template",
"id": "{ACCOUNT_TEMPLATE_ID}",
"is_enabled": true,
"name": "sample template",
"template": {
"account_type": "SAVING",
"bank_country": "US",
"currency": "USD",
"interest_product_id": "{ACCOUNT_PRODUCT_ID}",
"is_ach_enabled": true,
"is_card_enabled": true,
"is_p2p_enabled": true,
"overdraft_limit": 0
}
}
```
Note the returned `id` attribute. This is used for when creating an account.
### **Account Programs**
Some account types, such as `PREPAID` (reloadable or non-reloadable), require additional set up on the Synctera side, in order to assign immutable characteristics to any account created with the specified `type`. For example, certain types of prepaid cards are required to have funding limits, rail restrictions, and spend limits, through the lifecycle of the card/account. Account Programs do not require additional coding on the integrator side, but may be required as part of a specific use case, such as Prepaid Cards.
Account programs leverage Spend Controls, rail restrictions and other attributes, and are applied at the template level, to ensure any account created under the given program enforces the necessary controls depending on network, bank or program rules. If Account Programs are required for your use case, Synctera will provide the necessary set up and information. Fintechs must simply pass the correct `account_template_id` provided during Onboarding by your Implementation Manager, which will contain the program level settings required.
## Closing an Account
As end customers unenroll from a fintech’s service, their accounts with Synctera will need to be closed. Synctera’s [Initiate Closure](/v2/reference/initiateaccountclosure) API allows fintechs to close a customer account as well as all products in use by the customer and ensure all underlying transactions are handled appropriately, with just one API call.
Once closure is initiated - Synctera will:
* Check for active disputes and pending transactions, and pause on closure until resolution
* Stop further transactions, except for certain allowed scenarios (authorized payments, refunds, etc.)
* Deactivate cards and tokens.
* End recurring transactions.
* Write off negative balances and handle other closure‑related behaviors.
* Payout any interest due to the customer.
* Transfer remaining funds to a specified destination account. If the destination account is an external account via ACH, the system will wait on a 2 day return window to ensure payout settles to avoid returns to a closed account before closing
### Integrating with Account Closure:
The following curl example will walk you through integrating with Synctera’s account closure functionality, via the [Initiate Closure](/v2/reference/initiateaccountclosure) API. This assumes you have set up baseurl and apikey environment variables. See [Base URL](/v2/reference/need-to-know#environments) and [Authentication](/v2/reference/need-to-know#authentication) for instructions.
Closing an account requires the following integrations:
1. Integrate with Synctera’s [Initiate Closure](/v2/reference/initiateaccountclosure) API: This API call will start the closure process and the customer account’s status will be set to IN\_CLOSING. The fintech will have to provide a destination account for customer funds to be transferred to.
2. Subscribe to the [Account.UPDATED](/v2/docs/webhooks-guide) webhook: This webhook will transmit updates on the status of the account. Once all system checks pass, any underlying transactions have completed and any products in use have been deactivated, the customer’s account will be set to a status of CLOSED.
The following example illustrates how to format an [Initiate Closure](/v2/reference/initiateaccountclosure) request:
Call [POST/v2/accounts/](/v2/reference/initiateaccountclosure)[/initiate\_closure](/v2/reference/initiateaccountclosure) to close the account.
```shell Shell theme={"system"}
curl \
-X POST \
$baseurl/v0/accounts/{$ACCOUNT_ID}/initiate_closure \
-H "Idempotency-Key $IDEMPOTENCY_KEY" \
-H "Authorization: Bearer $apikey" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data-binary '
{
"destination_id": "ebdc0c82-3dae-4a85-b941-7bb9f0abd829",
"payment_method": "ACH",
"reason": “CUSTOMER_REQUEST_TRANSFER”,
"reason_details": “End customer is moving off our service ”,
}’
```
The following example illustrates the format of an [Initiate Closure](/v2/reference/initiateaccountclosure) response:
```shell Shell theme={"system"}
{
"access_status": "ACTIVE",
"access_status_last_updated_time": "2024-08-20T22:24:46.722385Z",
"account_closure": {
"case_ids": [],
"destination_id": "ebdc0c82-3dae-4a85-b941-7bb9f0abd829",
"payment_method": "ACH",
"reason": "CUSTOMER_REQUEST_TRANSFER",
"reason_details": "End customer is moving off our service",
"validation_responses": []
},
"account_number": "203749521694",
"account_purpose": "Demo account",
"account_type": "CHECKING",
"bank_account_id": "i7bc9089-3f5g-1cb0-a533-d19364fd2263",
"bank_routing": "931937896",
"creation_time": "2024-08-20T22:24:46.691379Z",
"currency": "USD",
"customer_type": "PERSONAL",
"id": "a4cad06c-0b28-42ea-bcd7-dead9373b82d",
"is_account_pool": false,
"is_ach_enabled": true,
"is_card_enabled": false,
"is_eft_ca_enabled": false,
"is_external_card_enabled": false,
"is_p2p_enabled": true,
"is_sar_enabled": false,
"is_security": false,
"is_synctera_pay_enabled": false,
"is_wire_enabled": false,
"last_updated_time": "2024-08-20T22:47:57.323435Z",
"nickname": "ACCOUNT CLOSURE DEMO ACCOUNT",
"open_date": "2024-08-20",
"status": "IN_CLOSING",
"tenant": "wrelqwerb_yhqwrkrp"
}
```
**Updating an Account Closure**
An account may require an update to the `payment_method` after the closure initiation has already been completed. Integrators can now PATCH the account closure with the new `payment_method` and `destination_id` details if necessary by using the [PATCH /v2/accounts/account\_id/initiate\_closure](https://docs.synctera.com/v2/reference/updateaccountclosure) endpoint.
Please see the [webhooks guide](/v2/docs/webhooks-guide) for further details on subscribing to the ACCOUNT.UPDATED webhook.
### Account Closure FAQs
1. **Will we be able to transfer the funds in multiple payments?** Yes. We are able to transfer the funds in multiple payments.
2. **Will the balance transfer happen immediately?** If there are no transactions on hold and no pending transactions, then the transfer will happen immediately after the Initiate Closure API was called. If there are transactions on hold or pending transactions, then the transfer will occur after these clear and the account will be closed.
3. **What happens if there is a return / refund after the account is closed?** Synctera will work with your Sponsor Bank to send a check to the last known address for any funds that come in after the account is closed or follow the bank process.
4. **Can customers still raise a dispute after the account is closed?** Yes, customers can raise a dispute after the account is closed. For card transactions, disputes can be filed up to 60 days from the statement date of the transaction in question. For ACH transactions, disputes can be raised up to 60 days from the settlement date of the transaction in question. If the investigation results in a chargeback, Synctera will manually send a check to the last known address for the transaction amount.
5. **Will a statement be issued after the account is closed?** Yes a statement will be issued at the end of the month, even after the account is closed. Smart cards will remain IN\_CLOSING, until the customer pays off the balance listed in the statement generated at the end of the month.
6. **Are there any controls to ensure that the funds are sent to a valid recipient?** Although we enforce industry standard fraud controls in our platform, we do not verify that the recipient is valid. We encourage the fintech to verify the recipient is valid, using its own data and or third party platforms.
7. **If you close an account without any underlying pending transactions or anything else that needs to be closed - how long until you receive the ‘closed’ webhook event?** You will receive it the next business day.
# MRDC
Source: https://docs.synctera.com/v2/docs/create-mobile-deposit-guide
MRDC - Mobile Remote Deposit Capture is a process that allows users to deposit checks into their accounts without having to go to a branch or ATM.
Customers can submit deposits by taking an image of the check and submitting it online using the Synctera platform. Synctera API provides mobile deposit endpoints. As a FinTech you'll have to implement a feature that allows a customer to take a photo of a check to be deposited.
## Steps to submit a deposit
The `curl` examples assume you have set up `baseurl` and `apikey` environment variables. See [Base URL](/v2/reference/need-to-know#environments) and [Authentication](/v2/reference/need-to-know#authentication) for instructions. Some examples depend on identifiers generated by previous steps. These are indicated like `{PERSON_ID}`.
### 1. Prompt customers to take the front and back image of the check on their mobile phone
As a FinTech you'll have to add a feature on your application that allows users to take the front and back images of a deposit to be submitted. After the user takes the front and back images of the check proceed to step 2
### 2. Upload images to Synctera
1. Upload front image and save the ID returned. Refer to [Create Document](/v2/reference/createdocument)
```shell Shell theme={"system"}
curl -X POST \
$baseurl/v0/documents \
-H 'Authorization: Bearer $apikey' \
--form 'name="test"' \
--form 'type="CHECK_IMAGE"' \
--form 'file=@"/path_to_image/front.jpeg"'
```
Synctera will return the following response:
```json JSON theme={"system"}
{
"creation_time": "2022-05-17T17:45:51.552888Z",
"description": "",
"file_name": "{FILE_NAME}",
"id": "{ID}",
"last_updated_time": "2022-05-17T17:45:51.552888Z",
"name": "back.jpeg",
"type": "CHECK_IMAGE"
}
```
2. Upload the rear image and save the returned id. Refer to [Create Document](/v2/reference/createdocument)
```shell Shell theme={"system"}
curl -X POST \
$baseurl/v0/documents \
-H 'Authorization: Bearer $apikey' \
--form 'name="{FILE_NAME}"' \
--form 'type="CHECK_IMAGE"' \
--form 'file=@"{PATH_TO_FILE}"'
```
Synctera will return the following response:
```json JSON theme={"system"}
{
"creation_time": "2022-05-17T17:45:51.552888Z",
"description": "",
"file": "{FILE}",
"id": "{ID}",
"last_updated_time": "2022-05-17T17:45:51.552888Z",
"name": "{NAME}",
"type": "CHECK_IMAGE"
}
```
### 3. Submit Deposit
Refer to [Submit Deposit](/v2/reference/createrdcdeposit)
```shell Shell theme={"system"}
curl -X POST \
$baseurl/rdc/deposits \
-H 'Authorization: Bearer $apikey' \
-H 'Content-Type: application/json' \
-d '
{
"account_id": "{ACCOUNT_ID}",
"front_image_id": "{FRONT_IMAGE_ID}",
"back_image_id": "{BACK_IMAGE_ID}",
"person_id": "{PERSON_ID}",
"check_amount": 235,
"deposit_currency": "USD"
}'
```
**Response**
```json JSON theme={"system"}
{
"account_id": "{ACCOUNT_ID}",
"back_image_id": "{BACK_IMAGE_ID}",
"check_amount": 235,
"creation_time": "2022-05-17T17:49:51.978751Z",
"date_captured": "2022-05-17T17:49:01.057994Z",
"date_processed": "2022-05-17T04:00:00Z",
"deposit_amount": 235,
"deposit_currency": "USD",
"front_image_id": "{FRONT_IMAGE_ID}",
"id": "{ID}",
"last_updated_time": "2022-05-17T17:49:51.978751Z",
"person_id": "{PERSON_ID}",
"status": "SUBMITTED",
"transaction_id": "{TRANSACTION_ID}",
"vendor_info": {
"content_type": "application/json",
"json": {
"amountDiscrepancyDetected": false,
"endorsementDetected": true,
"errors": [],
"frontImage": "{front_image_data}",
"ocrAccountNumber": "968788737",
"ocrAmount": "2.35",
"ocrAmountConfidence": "1000",
"ocrAmountStatus": "true",
"ocrCheckNumber": "0188",
"ocrCheckTranCode": "",
"ocrRoutingNumber": "021202337",
"ocrmicr": "d021202337d968782327c0188",
"ocrmicrConfidence": "910",
"ocrmicrStatus": "True",
"processDate": "2022-05-17T00:00:00-04:00",
"rearImage": "{rear_image_data}",
"referenceNumber": 202205,
"riskRating": null,
"riskRatingDescription": null,
"status": "40",
"statusDescription": "Pending",
"success": true
},
"vendor": "checkalt"
}
}
```
The deposit can be in any of the following states
* `SUBMITTED` the deposit has been successfully scanned by the OCR and submitted for processing
* `PENDING` The OCR engine detected issues such as amount discrepancy, duplicate deposit and fraud while scanning the image, a case will be created for manual review, please review the case to approve or reject the deposit request
* `FAILED` The OCR engine detected issues with the submitted deposit. Please retake the images and resubmit
* `REJECTED` The deposit has been rejected by the bank
## Funds availability rules
When the fintech and bank sign up for MRDC they can configure funds availability rule that allows portion of the amount to be available immediately and create a temporary hold with an effective release date
## Compliance for MRDC
To meet bank regulations, the fintech will be asked to share their image capture process with their partner bank. This may include providing a list of the libraries being used for the process and evidence that the check images are not being cached. In general, bank partners would like to ensure that images of checks are not stored for an extended period of time, if at all, and that any requests made with these images are done so securely.
# Credit Applications
Source: https://docs.synctera.com/v2/docs/credit-applications-guide
The Application API is used to collect applicants' data used for the credit application decision process.
As a fintech processes a credit application, they must update the content and status of the application. The application status must be in state 'CREDIT\_ACCEPTED\_BY\_CUSTOMER' before a credit account can be created. Credit account type include `LINE_OF_CREDIT`, `CHARGE_SECURED`, and `CHARGE_UNSECURED`.
A credit application consists of a primary applicant, with an option to add one or more co-applicant(s), application status, the account type the application is associated with, and the purpose of this application, which ranges from account opening to account management post issuance. The Credit Application spec includes more standardized information on applicants to help fintechs record the lifecycle of a credit application from initial submission to the resulting outcome, and share it with their sponsoring bank. That includes if credit was extended or not; if extended credit was accepted or declined by the client; once extended, what data information is used for the decisioning process; and, if the application is declined, what adverse action reasons were sent.
The application processing is a multi-step process. The following diagram illustrates all the states the application could be in. Upon creation of the credit application, the status is `APPLICATION_SUBMITTED` and can flow to other states as seen in the following diagram.
```mermaid theme={"system"}
graph TD;
APPLICATION_SUBMITTED --> CREDIT_APPROVED;
APPLICATION_SUBMITTED --> CREDIT_DENIED;
CREDIT_APPROVED --> CREDIT_ACCEPTED_BY_CUSTOMER;
CREDIT_APPROVED --> CREDIT_NOT_ACCEPTED_BY_CUSTOMER;
```
Once the application is in `CREDIT_ACCEPTED_BY_CUSTOMER` state, a credit account is now allowed to be created.
If the application is in the `CREDIT_DENIED` state, the fintech is required to provide an adverse action notice to the customer. This requirement is primarily for retail consumers, but may also be applicable to certain business customers. Please confirm with your sponsor bank's compliance policy. This requirement is applicable for KYC failure, credit decision rejection, and incomplete application.
If credit scores were pulled and utilized for credit decisions during application, please follow the examples [here](/v2/docs/credit-scores-guide) to record the scores and link to the related applications.
### Examples
1a. Application Submitted - Create an Application - `POST /v1/applications`
```bash Bash theme={"system"}
curl -X POST \
-H 'Authorization: Bearer $apikey' \
-H 'Content-Type: application/json' \
-d '
{
"account_type": "LINE_OF_CREDIT",
"applicants": [
{
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"is_primary": true
}
],
"purpose": "ACCOUNT_OPENING",
"status": "SUBMITTED",
"type": "CREDIT"
}' $baseurl/v1/applications
```
Sample response body:
```json JSON theme={"system"}
{
"account_type": "LINE_OF_CREDIT",
"applicants": [
{
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"is_primary": true
}
],
"purpose": "ACCOUNT_OPENING",
"status": "SUBMITTED",
"type": "CREDIT",
"creation_time": "2022-10-26T19:14:45.861687Z",
"id": "ebda67f0-e0a7-41e2-98ed-0617a1e815a6",
"last_updated_time": "2023-09-20T00:31:10.255042Z"
}
```
1b-i. Application Submitted, with credit scores used - Create a credit score record - `POST /v1/credit_scores`
```bash Bash theme={"system"}
curl -X POST \
-H 'Authorization: Bearer $apikey' \
-H 'Content-Type: application/json' \
-d '
{
"customer_id": "6a7ed697-4e8c-481c-b8e9-cfadde31342e",
"score": 725,
"score_requested_time": "2020-01-01T00:00:00Z",
"source_of_score": "ACCOUNT_OPENING",
"type": "FICO",
"vendor_name": "EQUIFAX",
"version": "8"
}' $baseurl/v1/credit_scores
```
Sample response body:
```json JSON theme={"system"}
{
"customer_id": "6a7ed697-4e8c-481c-b8e9-cfadde31342e",
"score": 725,
"score_requested_time": "2020-01-01T00:00:00Z",
"source_of_score": "ACCOUNT_OPENING",
"type": "FICO",
"vendor_name": "EQUIFAX",
"version": "8",
"creation_time": "2023-10-03T22:35:01.415125Z",
"id": "9534eebb-7d37-4d07-b219-8133d26e6fee",
"last_updated_time": "2023-10-03T22:35:01.415125Z"
}
```
1b-ii. Application Submitted, with credit scores used - Create an Application - `POST /v1/applications`
```bash Bash theme={"system"}
curl -X POST \
-H 'Authorization: Bearer $apikey' \
-H 'Content-Type: application/json' \
-d '
{
COMING SOON
}' $baseurl/v1/applications
```
Sample response body:
```json JSON theme={"system"}
{
COMING SOON
}
```
2a. Credit Approved - Update the application - `PATCH /v1/applications/{APPLICATION_UUID}`
```bash Bash theme={"system"}
curl -X PATCH \
-H 'Authorization: Bearer $apikey' \
-H 'Content-Type: application/json' \
-d '
{
"status": "CREDIT_APPROVED"
}' $baseurl/v1/applications/{APPLICATION_UUID}
```
Sample response body:
```json JSON theme={"system"}
{
"account_type": "LINE_OF_CREDIT",
"applicants": [
{
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"is_primary": true
}
],
"purpose": "ACCOUNT_OPENING",
"status": "CREDIT_APPROVED",
"type": "CREDIT",
"creation_time": "2022-10-26T19:14:45.861687Z",
"id": "ebda67f0-e0a7-41e2-98ed-0617a1e815a6",
"last_updated_time": "2023-09-20T00:31:10.255042Z"
}
```
3a. Customer accepts the credit offered - Update the application - `PATCH /v1/applications/{APPLICATION_UUID}`
```bash Bash theme={"system"}
curl -X PATCH \
-H 'Authorization: Bearer $apikey' \
-H 'Content-Type: application/json' \
-d '
{
"status": "CREDIT_ACCEPTED_BY_CUSTOMER"
}' $baseurl/v1/applications/{APPLICATION_UUID}
```
Sample response body:
```json JSON theme={"system"}
{
"account_type": "LINE_OF_CREDIT",
"applicants": [
{
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"is_primary": true
}
],
"purpose": "ACCOUNT_OPENING",
"status": "CREDIT_ACCEPTED_BY_CUSTOMER",
"type": "CREDIT",
"creation_time": "2022-10-26T19:14:45.861687Z",
"id": "ebda67f0-e0a7-41e2-98ed-0617a1e815a6",
"last_updated_time": "2023-09-20T00:31:10.255042Z"
}
```
Alternative Flows
2b. After Application Submitted, Credit is Denied - Fintech sends an Adverse Action Notice to Applicant - `POST /v1/adverse_actions`
```bash Bash theme={"system"}
curl -X POST \
-H 'Authorization: Bearer $apikey' \
-H 'Content-Type: application/json' \
-d '
{
"notification_time": "2020-05-19T21:14:27.434964Z",
"purpose": "ACCOUNT_OPENING",
"reasons": [
"TOO_MANY_INQUIRIES",
"INSUFFICIENT_CREDIT_HISTORY"
],
"related_resource_id": "9337a443-fa03-471c-ab05-b138c41dbd17",
"related_resource_type": "CUSTOMER"
}' $baseurl/v1/adverse_actions
```
Sample respones body:
```json JSON theme={"system"}
{
"notification_time": "2020-05-19T21:14:27.434964Z",
"purpose": "ACCOUNT_OPENING",
"reasons": [
"TOO_MANY_INQUIRIES",
"INSUFFICIENT_CREDIT_HISTORY"
],
"related_resource_id": "9337a443-fa03-471c-ab05-b138c41dbd17",
"related_resource_type": "CUSTOMER",
"creation_time": "2023-09-19T15:48:24.10184Z",
"id": "2fb2858b-f859-4dc8-9ad2-2a4e596fed89",
"last_updated_time": "2023-09-19T15:48:24.10184Z"
}
```
3b. After Application Submitted, Credit is Denied - Update the application - `PATCH /v1/applications/{APPLICATION_UUID}`
```bash Bash theme={"system"}
curl -X PATCH \
-H 'Authorization: Bearer $apikey' \
-H 'Content-Type: application/json' \
-d '
{
"status": "CREDIT_DENIED",
"adverse_action_id": "2fb2858b-f859-4dc8-9ad2-2a4e596fed89"
}' $baseurl/v1/applications/{APPLICATION_UUID}
```
Sample response body:
```json JSON theme={"system"}
{
"account_type": "LINE_OF_CREDIT",
"applicants": [
{
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"is_primary": true
}
],
"purpose": "ACCOUNT_OPENING",
"status": "CREDIT_DENIED",
"type": "CREDIT",
"adverse_action_id": "2fb2858b-f859-4dc8-9ad2-2a4e596fed89",
"creation_time": "2022-10-26T19:14:45.861687Z",
"id": "ebda67f0-e0a7-41e2-98ed-0617a1e815a6",
"last_updated_time": "2023-09-20T00:31:10.255042Z"
}
```
# Credit Scores
Source: https://docs.synctera.com/v2/docs/credit-scores-guide
Record the credit scores used for each credit decision and associate them with the customer and application, providing an FCRA-compliant audit trail.
## Overview
Accurately recording when a credit score is pulled and used for a credit decision is essential — and anchored in the Fair Credit Reporting Act (FCRA). The FCRA mandates the fair and accurate use of credit information. Because scores fluctuate over time, capturing the exact moment of retrieval guarantees decisions are based on current, relevant data and provides a transparent audit trail that protects both the institution and the applicant.
**A credit score record** captures a score returned by a vendor, the moment it was requested, and the decision it informed. It is always associated with a customer, and its `id` is linked to the credit application it supported.
Key characteristics:
* **Customer-scoped** — every credit score is tied to a `customer_id`.
* **Point-in-time** — `score_requested_time` records exactly when the score was retrieved.
* **Sourced** — `type`, `vendor_name`, and `version` identify where the score came from (e.g. FICO 8 from Equifax).
* **Application-linked** — attach one or more score `id`s to an application via its `credit_score_ids` field.
Provide all credit scores used for each credit decision across the account lifecycle: account opening, denied line-increase request, penalty-based APR increase, account closure, and more. If more than one score is used for an application, add all relevant score IDs to the application's `credit_score_ids` field.
## Prerequisites
This guide assumes you are familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
* Credit [applications](/v2/reference/patchapplication)
## The credit score object
| Field | Description |
| ------------------------------------- | ---------------------------------------------------------------------- |
| `id` | Unique identifier (read-only, assigned on creation). |
| `customer_id` | **Required.** The customer the score belongs to. |
| `score` | The numeric credit score. |
| `type` | The score type (e.g. `FICO`). |
| `vendor_name` | The bureau or vendor that provided the score (e.g. `EQUIFAX`). |
| `version` | The score model version (e.g. `8`). |
| `source_of_score` | The lifecycle event the score was pulled for (e.g. `ACCOUNT_OPENING`). |
| `score_requested_time` | When the score was retrieved from the vendor. |
| `creation_time` / `last_updated_time` | Timestamps (read-only). |
```json theme={"system"}
{
"id": "c387f46a-0a9e-44bc-82fa-aed68982e6a5",
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"score": 725,
"type": "FICO",
"vendor_name": "EQUIFAX",
"version": "8",
"source_of_score": "ACCOUNT_OPENING",
"score_requested_time": "2023-09-19T19:15:45.861687Z",
"creation_time": "2023-09-19T15:48:24.10184Z",
"last_updated_time": "2023-09-19T20:14:45.861687Z"
}
```
See the [API reference](/v2/reference/createcreditscores) for the full schema.
## Recording a credit score
When a FICO score is used for a decision on a Line of Credit application, record it with [POST /v2/credit\_scores](/v2/reference/createcreditscores):
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/credit_scores \
--data-binary '
{
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"score": 725,
"score_requested_time": "2023-09-19T19:15:45.861687Z",
"source_of_score": "ACCOUNT_OPENING",
"type": "FICO",
"vendor_name": "EQUIFAX",
"version": "8"
}'
```
The response includes the system-generated `id`:
```json theme={"system"}
{
"id": "c387f46a-0a9e-44bc-82fa-aed68982e6a5",
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"score": 725,
"score_requested_time": "2023-09-19T19:15:45.861687Z",
"source_of_score": "ACCOUNT_OPENING",
"type": "FICO",
"vendor_name": "EQUIFAX",
"version": "8",
"creation_time": "2023-09-19T15:48:24.10184Z",
"last_updated_time": "2023-09-19T20:14:45.861687Z"
}
```
Associate the score with the applicant on the Line of Credit application using [PATCH /v2/applications/\{APPLICATION\_ID}](/v2/reference/patchapplication). Add all relevant score IDs to `credit_score_ids`:
```shell theme={"system"}
curl \
-X PATCH \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/applications/{APPLICATION_ID} \
--data-binary '
{
"applicants": [
{
"is_primary": true,
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"credit_score_ids": ["c387f46a-0a9e-44bc-82fa-aed68982e6a5"]
}
]
}'
```
```json theme={"system"}
{
"id": "ebda67f0-e0a7-41e2-98ed-0617a1e815a6",
"account_type": "LINE_OF_CREDIT",
"type": "CREDIT",
"purpose": "ACCOUNT_OPENING",
"status": "SUBMITTED",
"applicants": [
{
"is_primary": true,
"customer_id": "4a666a01-d23a-47b1-8c20-2eb5a923da35",
"credit_score_ids": ["c387f46a-0a9e-44bc-82fa-aed68982e6a5"]
}
],
"creation_time": "2023-09-19T19:14:45.861687Z",
"last_updated_time": "2023-09-20T00:31:10.255042Z"
}
```
## Best practices
Record every credit score used in a decision, and set `score_requested_time` to when the score was actually pulled — scores change over time, and the timestamp anchors your FCRA audit trail.
* **Record all scores used** — if multiple scores inform one decision, capture each and add every `id` to the application's `credit_score_ids`.
* **Set an accurate source** — use `source_of_score` to reflect the lifecycle event (opening, line increase, APR change, closure).
* **Identify the score fully** — populate `type`, `vendor_name`, and `version` so reviewers know exactly which model produced the score.
* **Pair with adverse actions** — when a score leads to a denial or less favorable terms, also record an [adverse action](/v2/docs/adverse-actions-guide).
## Related guides
Record the reasons behind an adverse credit decision.
Link credit scores to the applications they inform.
The credit accounts these decisions open.
## API reference
* [Create a credit score](/v2/reference/createcreditscores)
* [List credit scores](/v2/reference/listcreditscores)
* [Get a credit score](/v2/reference/getcreditscore)
* [Update an application](/v2/reference/patchapplication)
# Custom Cards
Source: https://docs.synctera.com/v2/docs/custom-card-images
Synctera offers the ability to create physical cards with custom images, such as customers' personal photos.
To do this you must have a card program with this feature enabled. To check if custom images are enabled, you can perform a `GET /v0/cards/products`. Any entry in the list of card products with an `image_mode` field supports custom images.
All custom card images must be in JPEG format.
### Card product image mode
If the card product has an `image_mode` then it supports custom images. The only supported `image_mode` is `"REQUIRED_APPROVED_FIRST"`. In this mode card creation requests using that card product must include an image ID and the image ID included in the card creation request must refer to an image which is in the `"APPROVED"` state.
## Image management
Custom card images are managed independently of the cards themselves. It is only during issuing a card that the images are associated with cards.
Card images start in the `"NOT_UPLOADED"` state and then transition to the `"UNREVIEWED"` state and finally to either `"APPROVED"` or `"REJECTED"`.
```mermaid mermaid theme={"system"}
%%{init: {"fontFamily": "sans-serif"}}%%
graph LR
NOT_UPLOADED-->UNREVIEWED
UNREVIEWED-->APPROVED
UNREVIEWED-->REJECTED
APPROVED-->REJECTED
```
Images that are `"APPROVED"` maybe be updated to `"REJECTED"` but not the other way around.
### Create an image entity
Uploading a custom card image is a two-step process. First, you must create a card image entity. This is done by performing a [`POST /v0/cards/images`](/v2/reference/createcardimage). The request body must contain the customer ID and the ID of a card product that supports custom images.
```json JSON theme={"system"}
{
"customer_id": "{{customer_id}}",
"card_product_id": "{{card_product_id}}"
}
```
This returns the newly-created card image entity in the `"NOT_UPLOADED"` state with its UUID.
```json JSON theme={"system"}
{
"customer_id": "{{customer_id}}",
"id": "{{image_id}}",
"status": "NOT_UPLOADED"
}
```
### Upload the image data
The second step of uploading the card image is to send the binary JPEG image data. You can do this by performing a [POST /v0/cards/images/\{image\_id}/data](/v2/reference/uploadcardimagedata). Put the binary image data in the body of this request and be sure to set the `Content-Type` header to `image/jpeg`.
In the response you should see that the image is now in the `"UNREVIEWED"` state.
Once you have uploaded the image data for an image it can not be changed. If you want to upload a new image instead you must create a new image entity with a new ID. You must start the two-step process again at the first step.
#### Acceptable images
Images that you upload are subject to review and will not be printed on cards until they are approved. Prohibited images include but are not limited to:
* Sexual subject matter of any nature
* Political subject matter of any nature
* Offensive racial/prejudicial subject matter of any nature
* Offensive religious subject matter of any nature
* Advertising of any nature1
* Self-promotion of any nature (e.g., personal business card)
* Copyrighted material of any nature
* Branded products/services, including abbreviations, acronyms, and/or symbols of any nature
* Solicitations, including telephone numbers or services of any nature (e.g., 900 or 800 numbers)
* Celebrities/musicians/athletes/entertainers/public figures, etc., of any nature1 • Affiliation with groups that are determined to be of a “socially unacceptable” nature, including scenes, names, or symbols
* Subject matter of any nature that might result in card acceptance confusion by merchants
* Subject matter of any nature that might result in card fraud
* Any graphic design element that might reflect poorly or might engender hostility toward or derision of all or any of the Mastercard family of brand
### Get the card image
After it is created, you can look up the image entity by performing a [`GET /v0/cards/images/{image_id}`](/v2/reference/getcardimagedetails). You can also list all of a customer's images by performing a `GET /v0/cards/images?customer_id={{customer_id}}`.
To get the JPEG image data, perform a [`GET /v0/cards/images/{{card_image_id}}/data`](/v2/reference/getcardimagedata).
### Image review
Once the image data has been uploaded, it is ready to be reviewed. This is typically done through the Synctera application. Authorized users can log in, click on "Cases" in the navigation bar and then click on "Custom Card Review". From here they can preview the images as they would appear on a card and approve or reject them. In Synctera's sandbox, developers can review their own images by performing a [PATCH /v0/cards/images/\{image\_id}](/v2/reference/updatecardimagedetails). The content of this request is either:
```json JSON theme={"system"}
{
"status": "APPROVED"
}
```
or:
```json JSON theme={"system"}
{
"status": "REJECTED"
}
```
## Card Creation
When you have an image ready for a card, you can create the card in the usual way by performing a [`POST /v0/cards`](/v2/reference/issuecard) using the card product that supports custom images and specifying the image ID in the `card_image_id` field.
```json JSON theme={"system"}
{
"form": "PHYSICAL",
"type": "DEBIT",
"customer_id": "{{customer_id}}",
"card_image_id": "{{image_id}}",
"card_product_id": "{{card_product_id}}",
"account_id": "{{account_id}}"
}
```
Note that the response will not include some fields that are available when creating cards that don't have custom images. Specifically, the last four digits of the PAN and the expiration month and year will not be included. This is because the cards are not fully created until the image is processed and image processing happens once per day. If everything goes normally, these fields will be available the following day if you perform a [`GET /v0/cards/{{card_id}}`](/v2/reference/getcard). You can also get these updated fields as soon as the card is fully created via a webhook.
# Customer Journey
Source: https://docs.synctera.com/v2/docs/customer-journey
Most Synctera APIs act on resources whose IDs are specified in requests.
## Overview
These resources must exist. This means there is a sequence to calling the APIs to get from a clean slate to a successful transaction.
This document is aimed at capturing that sequence, which is a reflection of a typical customer journey. We have provided the exact API details here but we strongly encourage you to read the Guides mentioned below to understand the dependencies, alternatives, configuration needs and additional information about these products.
You can also explore and try out the [Postman collection](https://www.postman.com/synctera/workspace/synctera-public-workspace/collection/19487588-931708f8-bc42-4051-8a02-4e9104d984e4?action=share\&creator=19487588) once you have an API key.
## Before You Begin
### Get an API Key
If you don't already have an API key, you will need one in order to call the APIs. If you have signed up for the t-minus10 program ([click here](https://drive.google.com/file/d/1qPYNx4Ij1el5lIKbfJcJttQJb4Ot9xny/view?usp=sharing) for more details on t-minus10) you can self-serve an API key by following the "Sandbox" or "Live testing" links in the top navigation. If you are already working with the Synctera team, you an also reach out to your contact at Synctera to get an API key.
**Self-serve**:
[https://app.synctera.com/admin/developer-tools/api-keys](https://app.synctera.com/admin/developer-tools/api-keys)
[https://app-sandbox.synctera.com/admin/developer-tools/api-keys](https://app-sandbox.synctera.com/admin/developer-tools/api-keys)
Once an API key is issued in Sandbox, you can immediately start calling the APIs below. In Live Testing or Production, your FinTech must first pass KYB.
### KYB Your FinTech
In Sandbox - you're done! KYB was automatically passed for your Sandbox workspace.
In Live Testing or Production, you need to have run a successful KYB on your Fintech if you want to issue cards or move money via ACH / Internal transfers.
If you are on t-minus 10, you need to do this by filling in [Business information](https://app.synctera.com/admin/my-fintech/address) and [Owner details](https://app.synctera.com/admin/my-fintech/owners) and then [Run KYB](https://app.synctera.com/admin/my-fintech/kyb) using the t-minus 10 platform.
If you are not on t-minus10, your contact at Synctera will collect the required information to run KYB for you.
## Guides and APIs
These are customers of the Financial Service that use Synctera APIs. Customers can either be Individuals or Businesses. If your customers are businesses, create a business.
**Guide**:
**APIs**:
Customers or Businesses who are onboarded to your platform will need to accept disclosures for legal needs. You will work with Synctera Implementation & Onboarding team to identify the disclosures needed for your company.
**Guide**:
**API**: [Create Disclosure](/v2/reference/createdisclosure)
The next step is to ensure that you know your customer and they are who they claim to be. Run KYC if your customer is an individual. Run KYB if your customer is a business.
As part of this, you can also enroll the user for Watchlists.
**Guide**:
**APIs**:
Account templates define basic characteristics of an account. This is a mandatory step to create an account.
You can also use Account Products which can be included in the account template to capture the Interest / Fee aspects of the account.
**Guide**:
**APIs**:
Once a Customer is created, Verified, and an account template is created, you can now create an account for the customer / business.
You can use Account relationships to create further relationships on the accounts - e.g Joint Account Holder.
**Guide**:
**APIs**:
Once an account is created, the next step is to move funds from an external bank account to move funds into the customer’s account at the Fintech.
**Guide**:
**APIs**:
To move funds into a customer account, you would typically link a customer's external account and move funds from there. Alternatively, as a convenience in the sandbox environment, you can also create an internal account to do this. Internal accounts in production would typically be used for specific needs such as issuing promotional credits to your customers. To create an internal account, use this API.
**API**:
Once an external account is linked (or you created an internal account as above), you can move funds from an external account into the customer’s account at the Fintech.
**API**:
Once the account is funded, the customer access the money on the account using cards or ACH. Users can be issued multiple virtual cards and a physical card per account.
You will have to use Marqeta widgets for accessing card details. You also can issue custom Cards with images uploaded by the user.
**Guides**:
**API**:
You can also move money between accounts held in the Fintech.
**API**:
# Developer Setup
Source: https://docs.synctera.com/v2/docs/dev-setup
## Developer Setup
Integrating your FinTech application with Synctera starts with a development setup:
* Signing up for a Synctera account
* Accessing our documentation and development tools
* Generating API keys
* Trying out our **sandbox**, **production**, and **t-minus10 live** environments
### Sign Up For a Synctera Account and Generate API Keys
Integrating your FinTech application with Synctera starts as soon as you finalize your fintech information and submit your profile for access to your developer environment:
1. [Sign up](https://app.synctera.com/sign-up) for your account.
2. Under Your Workspace, click **Generate key** to generate a sandbox API key.
3. Click **Copy** to copy the API key to your clipboard
Once you close the API key pop-up window, you won't be able to view the key. Remember to store your key in a safe location.
You're all set! Your account is set up, and you’re ready to use your API key in your staging environment.
### Look at Available Synctera APIs
To see the available Synctera APIs, take a look at:
* Our [API reference documentation](/reference).
You can use all of these APIs in our sandbox, but your FinTech must be subscribed to the appropriate Synctera products to use APIs for production. Subscribing to Ledger as a Service, for example, provides production access to the Accounts API.
### Using Your API Key
Use the API key you received as authorization for each API request you make to our sandbox. Add it to the request header as shown in this snippet from a cURL request:
`-H 'Authorization: Bearer {API_KEY}'`
### Create a Customer
The following cURL request creates a new customer, specifying name, addresses, data of birth, Social Security number, and other pertinent information. Be sure to use your API key to replace \{API\_KEY} below for authorization:
```bash theme={"system"}
curl -X POST https://api-sandbox.synctera.com/v0/customers \
-H 'Authorization: Bearer {API_KEY}' \
-H 'Content-Type: application/json; charset=utf-8' \
--data-binary @- << EOF
{
"first_name": "Jane",
"last_name": "Doe",
"middle_name": "Fawn",
"legal_address": {
"default_address_flg": true,
"type": "home",
"house_number": "781",
"address_line_1": "Street Rd.",
"address_line_2": "Apartment 32",
"city": "Centerville",
"state": "CO",
"postal_code": "40993",
"country_code": "US"
},
"shipping_address": {
"default_address_flg": false,
"type": "work",
"house_number": "485",
"address_line_1": "Boulevard Ave.",
"city": "Centerville",
"state": "CO",
"postal_code": "40995",
"country_code": "US"
},
"dob": "2000-01-01",
"ssn": "123-45-6789",
"email": "DeerMe32@gmail.com",
"mobile_phone_number": "+17520343695",
"status": "ACTIVE"
}
EOF
```
The request returns information about the newly created that includes a customer ID used to specify the customer in other requests:
```json theme={"system"}
{
"dob":"2000-01-01",
"email":"DeerMe32@gmail.com",
"first_name":"Jane",
"id":"e79ca9ae-25bd-421e-92a2-22d118397799",
"last_name":"Doe",
"legal_address": {
"address_line_1":"Street Rd.",
"address_line_2":"Apartment 32",
"city":"Centerville",
"country_code":"US","default_address_flg":true,
"id":"a765479c-ef5f-405b-9e0a-746bdd1e4974",
"postal_code":"40993",
"state":"CO","type":"home"
},
"middle_name":"Fawn",
"shipping_address": {
"address_line_1":"Boulevard Ave.",
"city":"Centerville",
"country_code":"US",
"default_address_flg":false,
"id":"8cf2f320-dee9-4e44-87be-b1296ce1fa9f",
"postal_code":"40995",
"state":"CO","type":"work"
},
"ssn":"6789",
"status":"ACTIVE"
}
```
## Synctera Development Tools
Synctera offers an SDK with client libraries at:
For current status of API service, look at:
# Digital Wallets
Source: https://docs.synctera.com/v2/docs/digital-wallets
Through Synctera, you can offer your end customers the ability to add cards to their digital wallet, such as Apple Pay or Google Wallet.
## Overview
This provides a convenient way for cardholders to store and use their payment cards securely. When the card is added to the digital wallet, the card’s sensitive data, i.e. the PAN, expiration date and CVV, is replaced with a token. This token serves as a reference to the card, and is used when the cardholder makes a payment using the digital wallet.
This guide describes what features can be implemented as part of the Digital Wallet & Tokenization process:
how a card can be added to a digital wallet an
functions (e.g. token suspension) and webhooks for token status changes
The Digital Wallet & Tokenization feature can only be tested in the production environment.
## Prerequisites
Before you can launch the digital wallet feature, the following steps must be completed:
1. Your card BIN is tokenized - Synctera works with your sponsor bank to get the BIN tokenized with the network.
2. You have submitted the required assets (such as digital wallet card art) to Synctera
3. For in-app provisioning, you have gained entitlement access from the digital wallet providers, added the feature to your app (integrated with digital wallets and Synctera), and gained app UI/UX approval from the digital wallet providers
4. You have fulfilled the testing requirements
Synctera’s Implementation & Onboarding Team will guide you through this process.
## Adding a Card to a Digital Wallet
The integration steps depend on the provisioning method you want to support. The first two methods can be enabled directly on the card product, whereas in-app provisioning requires additional integration steps.
### Manual provisioning:
The cardholder opens the wallet app (Apple Pay / Google Wallet) on a mobile device and manually adds the virtual or physical card to the wallet by typing in the card details.
### Card-on-file:
Card-on-file tokens cover two distinct scenarios:
**(a) Device-to-device propagation:** A cardholder has already added a card to one device (e.g., iPhone) and adds it to another device (e.g., Apple Watch, iPad). The wallet provider uses the existing on-file card details to provision a separate token for the new device. This is wallet-mediated, so step-up verification (yellow path) can still be performed through the wallet UI when required.\
\
**(b) Merchant tokenization:** A cardholder saves their card with a merchant (e.g., Amazon, Netflix, Spotify, Facebook/Meta Pay), and the merchant tokenizes the card via the card network — outside of any consumer wallet application. The token is then pushed to Synctera by the network. Because there is no consumer wallet interface to surface a step-up challenge, **yellow path step-up verification (OTP) is not feasible for merchant card-on-file tokens**. The network assumes the merchant has authenticated the user (typically via account login). For these tokens, **3DS is the practical fraud control on the transaction itself** — the protections shift from the provisioning step to the transaction-authentication step.
### In-app provisioning:
This method is also called push-provisioning or instant issuance. Cardholder can “push” the card to the digital wallet from within your app, for example through a “Add to Wallet” button. You need to integrate with the digital wallet provider (instructions provided by Synctera) and with the Synctera platform by calling the correct endpoint based on the type of device:
For Apple Pay compatible devices, call:
```bash theme={"system"}
POST /v0/cards/{card_id}/digital_wallet_tokens/applepay
```
Sample Request:
```json JSON theme={"system"}
{
"device_type": "MOBILE_PHONE",
"provisioning_app_version": "2.13.7",
"certificates": [
"MIIEPDCCA+KgAwIBAgICEAAwCQYHKoZIzj...",
"MIIDZjCCAw2gAwIBAgIJAJx22AGaEPSgMA..." ],
"nonce": "vXWJaBidcTLaJJCF",
"nonce_signature": "jD4Aphu+93N2wbBn"
}
```
For Google Wallet compatible devices, call:
```bash theme={"system"}
POST /v0/cards/{card_id}/digital_wallet_tokens/googlepay
```
Sample Request:
```json JSON theme={"system"}
{
"device_type": "MOBILE_PHONE",
"provisioning_app_version": "2.13.3",
"wallet_account_id": "ae25OGhjZTk2dsr452dgsr51",
"device_id": "W85OGhjZTk2dsr452dgsr51j"
}
```
See [API reference](/v2/reference/createdigitalwalletapple) for more information about Digital Wallet Tokens API.
### Token approval process - step-up verification:
All token provisioning requests go through a token approval process, where the legitimacy of the request is scored. Based on the score, the request can either be automatically approved/declined or step-up verification is required. During step-up verification, the cardholder gets a one-time passcode (OTP) through email or SMS, based on preferences, and enters the code in the wallet for verification. This requires that all cardholders have an email and mobile phone number on file (enforced through the Synctera onboarding journey).
Synctera offers support through the Synctera UI for resolving any issues that your customers may run into while adding their card to their digital wallet.
## Token lifecycle
During its lifecycle, a card token can have the following states:
* `REQUESTED`
* `REQUEST_DECLINED`
* `ACTIVE` - only from `REQUESTED`/`SUSPENDED` states
* `SUSPENDED` - only from `ACTIVE` state
* `TERMINATED` - irrevocable state
To change the state of a token, you can call:
```bash theme={"system"}
PATCH /v0/cards/digital_wallet_tokens/{digital_wallet_token_id}
```
Sample Request:
```json JSON theme={"system"}
{
"token_status": "ACTIVE"
}
```
Sample Response:
```json JSON theme={"system"}
{
"card_id": "ea4fe485-b688-4fc8-b861-a136245ab931",
"device_id": "044B3013EE56800192111170603027118DE08E4001501681",
"device_type": "MOBILE_PHONE",
"id": "de0ebd60-4dd5-413a-a0cc-afbd91798f7c",
"last_modified_time": "2022-05-06T18:58:02.587813Z",
"requested_time": "2022-05-06T18:11:19Z",
"state": "ACTIVE",
"type": "APPLE_PAY"
}
```
Anytime a token state changes, a webhook event is generated:
* `card.digitalwallettoken.created` - This webhook is published whenever the provisioning of a new token is requested, regardless of what provisioning method was used (manual, card-on-file, in-app).
* `card.digitalwallettoken.updated` - This webhook is published whenever a digital wallet token status is updated. This update can be triggered directly by the PATCH endpoint or as a result of another action, e.g. when a pending token provisioning request gets approved or when a card is terminated or reissued.
To get a list of all tokens that have been provisioned for a card, including information on token state, call:
```bash theme={"system"}
GET /v0/cards/digital_wallet_tokens
```
Sample Response:
```json JSON theme={"system"}
{
"digital_wallet_tokens": [
{
"card_id": "ea4fe485-b688-4fc8-b861-a136245ab931",
"device_id": "044B3013EE56800192111170603027118DE08E4001501681",
"device_type": "MOBILE_PHONE",
"id": "de0ebd60-4dd5-413a-a0cc-afbd91798f7c",
"last_modified_time": "2022-05-06T18:58:02.587813Z",
"requested_time": "2022-05-06T18:11:19Z",
"state": "ACTIVE",
"type": "APPLE_PAY"
},
{
"card_id": "6cfe1b97-f737-49e3-8aee-55bd19afa739",
"device_id": "044B3013EE56800192111170603027118DE08E4001501681",
"device_type": "MOBILE_PHONE",
"id": "8fdf0a38-055d-41bd-a238-d79269335b7f",
"last_modified_time": "2022-06-08T23:46:38.736908Z",
"requested_time": "2022-05-16T20:48:18Z",
"state": "ACTIVE",
"type": "ANDROID_PAY"
}
]
}
```
To get the details of a specific token, call:
```bash theme={"system"}
GET /v0/cards/digital_wallet_tokens/{digital_wallet_token_id}
```
Sample Response:
```json JSON theme={"system"}
{
"card_id": "6cfe1b97-f737-49e3-8aee-55bd19afa739",
"device_id": "044B3013EE56800192111170603027118DE08E4001501681",
"device_type": "MOBILE_PHONE",
"id": "8fdf0a38-055d-41bd-a238-d79269335b7f",
"last_modified_time": "2022-06-08T23:46:38.736908Z",
"requested_time": "2022-05-16T20:48:18Z",
"state": "ACTIVE",
"type": "ANDROID_PAY"
}
```
* When a card is reissued through the POST /cards endpoint, the token associated with the old card is automatically transferred to the new card
* When a card is terminated through the PATCH /cards endpoint, the token associated with the card is automatically `TERMINATED`
* When a virtual card moves to a new cardholder through the PATCH /cards endpoint, the tokens associated with the card that were provisioned to the old cardholder are automatically `TERMINATED`
See [API reference](/v2/reference/createdigitalwalletapple) for more information about Digital Wallet Tokens API.
# Document Storage
Source: https://docs.synctera.com/v2/docs/document-storage-guide
Synctera's document storage service lets you upload, list, and retrieve files — with optional encryption for sensitive documents such as government IDs.
## Overview
Document storage is a simple file service: you upload documents, list them, and retrieve them later. A *document* is any file you or a customer needs to store — a PDF, a Word document, a spreadsheet, a JPEG, almost anything.
**Uploading a document** returns a record with an `id` you can use to fetch the file back byte-for-byte. Documents can also be attached to other resources (for example, business formation documents or EDD supporting evidence).
Key characteristics:
* **Any file type** — store PDFs, images, spreadsheets, and more, up to **32 MB** per document.
* **Optionally encrypted** — set `encryption` to `REQUIRED` to apply additional encryption treatment for PII and government IDs.
* **Multipart upload** — files are sent as `multipart/form-data`, exactly how a browser upload works.
* **Retrievable, append-only** — documents can be uploaded, listed, and retrieved; deletion is not currently supported.
Set `encryption` to `REQUIRED` whenever you store PII. This applies additional encryption treatment to the stored object. Document types that should always be encrypted include US Social Security Numbers, other national ID numbers, driver's license numbers and images, passport numbers and images, and any similar government ID.
## Prerequisites
This guide assumes you are familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
## The document object
An uploaded document is described by a JSON record:
| Field | Description |
| ------------------------------------- | ----------------------------------------------------------------------------- |
| `id` | Unique identifier (read-only, assigned on upload). Used to retrieve the file. |
| `file_name` | The original filename of the uploaded file. |
| `name` | Display name for the document. |
| `description` | Optional description. |
| `creation_time` / `last_updated_time` | Timestamps (read-only). |
```json theme={"system"}
{
"id": "2a1e97a8-96a5-4b24-929a-2f8e4dc6851e",
"file_name": "hello.txt",
"name": "hello.txt",
"description": "",
"creation_time": "2022-04-06T14:18:10.123265Z",
"last_updated_time": "2022-04-06T14:18:10.123265Z"
}
```
## Managing documents
Send a `POST /v2/documents` request with `Content-Type: multipart/form-data`. The only required form parameter is the file data itself; add `encryption=REQUIRED` for sensitive files. See [Create Document](/v2/reference/createdocument).
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-F encryption=REQUIRED \
-F file=@secret.png \
https://api.synctera.com/v2/documents
```
A minimal, non-encrypted upload:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-F file=@hello.txt \
https://api.synctera.com/v2/documents
```
The response is the document record described above. Documents cannot exceed **32 MB**.
Fetch the exact file contents that were uploaded with [GET /v2/documents/\{document\_id}/contents](/v2/reference/getdocument):
```shell theme={"system"}
curl \
-H "Authorization: Bearer $apikey" \
https://api.synctera.com/v2/documents/2a1e97a8-96a5-4b24-929a-2f8e4dc6851e/contents
```
The response streams the file back with its original `Content-Disposition` filename and content type — whether that is a 13-byte text file or a 30 MB PDF.
Retrieve a paginated list of all uploaded documents with [GET /v2/documents](/v2/reference/listdocuments):
```shell theme={"system"}
curl \
-H "Authorization: Bearer $apikey" \
https://api.synctera.com/v2/documents
```
## Best practices
* **Encrypt PII by default** — set `encryption=REQUIRED` for any document containing sensitive personal or government identifiers.
* **Keep files under 32 MB** — split or compress larger files before upload.
* **Store, don't inline** — attach source documents here and reference them from other resources (KYB, EDD) rather than embedding raw data.
* **Record the returned `id`** — you cannot delete a document, so track the `id` to retrieve or reference it later.
## Related guides
Attach business formation and MSB documentation.
Link supporting documents to an EDD submission.
Verify a customer's identity using their documents.
## API reference
* [Create a document](/v2/reference/createdocument)
* [Get a document](/v2/reference/getdocument)
* [List documents](/v2/reference/listdocuments)
# Document Verification
Source: https://docs.synctera.com/v2/docs/document-verification
Document verification is a step-up method that verifies a customer's identity from their government-issued documents and a live selfie — improving conversion for customers who fail automated KYC.
## Overview
Alongside identity verification and watchlist monitoring, Synctera's verification solution includes **document verification**. As described in the [verification guide](/v2/docs/kyc-kyb-verification#verification-status-and-verifying-customers), it is recommended as a *step-up* method for customers who cannot be verified automatically from basic CIP information (name, address, date of birth, government ID).
Synctera has partnered with [Socure](https://www.socure.com) to verify thousands of document types from over 100 countries. By capturing the front and back of a document plus a live selfie, document verification improves conversion for customers who would otherwise land in manual review, while reducing risk by further confirming the customer's identity.
Key characteristics:
* **Step-up** — best used for verifications flagged `REVIEW`, to resolve issues automatically rather than routing to manual review.
* **Document-based** — validates a government-issued document and matches it to a live selfie.
* **Conversion-boosting** — recovers customers who fail initial automated KYC.
* **SDK-driven** — document capture happens through Socure's platform SDKs.
Document verification increases conversion for customers flagged for review in their initial KYC verification. To build out your document verification flow using the Socure SDK, contact your Synctera sales representative.
### Socure SDKs
Socure provides SDKs for multiple platforms to simplify integration:
* WebSDK
* Android SDK
* iOS SDK
* React SDK
## Prerequisites
This guide assumes you have:
* Created a [personal customer](/v2/docs/create-a-personal-customer)
* Recorded a [disclosure](/v2/docs/record-disclosure-acceptance)
* [Verified the customer](/v2/docs/kyc-kyb-verification)
You should also be familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
## Example: document verification as a step-up
This example creates a customer who is flagged for `REVIEW` on initial verification, then uses Socure's document verification to resolve the issue automatically — decreasing manual review time and increasing conversion.
Create a record for the customer with [POST /v2/persons](/v2/reference/createperson):
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/persons \
--data-binary '
{
"first_name": "Christopher",
"middle_name": "James",
"last_name": "Albertson",
"dob": "1985-06-14",
"email": "chris@example.com",
"phone_number": "+16045551212",
"ssn": "456-78-9999",
"legal_address": {
"address_line_1": "123 Main St.",
"city": "Beverly Hills",
"state": "CA",
"postal_code": "99999",
"country_code": "US"
},
"is_customer": true,
"status": "ACTIVE"
}'
```
See the [Create a Personal Customer](/v2/docs/create-a-personal-customer) guide for details.
Display a disclosure informing the customer that you are collecting personal data to be shared with a third party for identity verification, then record it with [POST /v2/disclosures](/v2/reference/createdisclosure):
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/disclosures \
--data-binary '
{
"person_id": "7ef75751-e372-4c12-9b02-b9e4b1faaac9",
"type": "KYC_DATA_COLLECTION",
"version": "1.0",
"event_type": "ACKNOWLEDGED",
"disclosure_date": "2022-03-17T17:04:34Z"
}'
```
See the [Record Disclosure Acceptance](/v2/docs/record-disclosure-acceptance) guide for details.
With the customer created and consent captured, run verification with [POST /v2/verifications/verify](/v2/reference/verify):
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/verifications/verify \
--data-binary '
{
"person_id": "7ef75751-e372-4c12-9b02-b9e4b1faaac9",
"customer_ip_address": "184.233.47.237",
"customer_consent": true
}'
```
Consent must come directly from the customer.
The response returns a verification for each check run:
```json theme={"system"}
{
"verification_status": "REVIEW",
"verifications": [
{
"id": "05e2ddf3-d172-450e-9cf3-7a34f76a414f",
"person_id": "7ef75751-e372-4c12-9b02-b9e4b1faaac9",
"verification_type": "IDENTITY",
"result": "REVIEW",
"details": [
{
"description": "Address cannot be resolved to the individual",
"label": "Address",
"result": "WARN",
"vendor_code": "R705"
},
{
"description": "Email address can be resolved to the individual",
"label": "Email",
"result": "PASS",
"vendor_code": "I556"
}
],
"verification_time": "2022-03-14T18:34:59.91272Z",
"creation_time": "2022-03-14T18:34:59.918188Z",
"last_updated_time": "2022-03-14T18:34:59.918188Z"
},
{
"id": "a24a16a2-4711-4486-8049-787462c61ffc",
"person_id": "7ef75751-e372-4c12-9b02-b9e4b1faaac9",
"verification_type": "WATCHLIST",
"result": "ACCEPTED",
"details": [
{
"description": "Global Watchlist sources selected are not correlated with the input identifiers",
"label": "Watchlist",
"result": "PASS"
}
],
"verification_time": "2022-03-14T18:34:59.91272Z",
"creation_time": "2022-03-14T18:34:59.918188Z",
"last_updated_time": "2022-03-14T18:34:59.918188Z"
}
]
}
```
Note the issue flagged on the `IDENTITY` verification: *Address cannot be resolved to the individual*. This warning marks the customer's verification status as `REVIEW`. Document verification, run next, can resolve it automatically.
Begin a document verification session with [POST /v2/verifications/docv\_session](/v2/reference/docvsession), passing the `person_id`:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/verifications/docv_session \
--data-binary '
{
"person_id": "7ef75751-e372-4c12-9b02-b9e4b1faaac9"
}'
```
The response contains a `session_token` to provide to the Socure SDK and a `url` the customer can use to upload their documents via Socure's web portal:
```json theme={"system"}
{
"session_token": "907ce33a-c564-454e-a538-804efe31e6ac",
"url": "https://verify.socure.com/session/907ce33a-c564-454e-a538-804efe31e6ac"
}
```
To build out your document verification flow using the Socure SDK, contact your Synctera sales representative.
Once the customer has uploaded their documents, send another verification request (as in step 3), using the `session_token` to populate the `document_id`:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/verifications/verify \
--data-binary '
{
"person_id": "7ef75751-e372-4c12-9b02-b9e4b1faaac9",
"customer_ip_address": "184.233.47.237",
"document_id": "907ce33a-c564-454e-a538-804efe31e6ac",
"customer_consent": true
}'
```
Providing a `document_id` runs a KYC verification using the documents supplied to the Socure SDK:
```json theme={"system"}
{
"verification_status": "ACCEPTED",
"verifications": [
{
"id": "05e2ddf3-d172-450e-9cf3-7a34f76a414f",
"person_id": "7ef75751-e372-4c12-9b02-b9e4b1faaac9",
"verification_type": "DOCUMENT_VERIFICATION",
"result": "ACCEPTED",
"details": [
{
"description": "Minimum required information extracted from document Barcode",
"label": "Document Verification",
"result": "PASS",
"vendor_code": "I831"
},
{
"description": "Document image correlates with self-portrait",
"label": "Document Verification",
"result": "PASS",
"vendor_code": "I836"
},
{
"description": "Socure's document verification model recommends accepting the individual",
"label": "Document Verification",
"result": "PASS"
}
],
"verification_time": "2022-03-14T18:34:59.91272Z",
"creation_time": "2022-03-14T18:34:59.918188Z",
"last_updated_time": "2022-03-14T18:34:59.918188Z"
}
]
}
```
The `DOCUMENT_VERIFICATION` response confirms the document image correlates with the selfie and that Socure recommends accepting the individual. A customer initially flagged for manual review is now verified automatically.
Once verified, the customer is ready for the [account creation guide](/v2/docs/create-accounts-guide).
## Best practices
* **Reserve it for `REVIEW`** — use document verification as a step-up for customers flagged for review, not as the default path for everyone.
* **Capture consent first** — record the KYC data collection disclosure and send `customer_consent` on every verification request.
* **Start a session per customer** — call `docv_session` to obtain the `session_token` and `url`, then reuse the `session_token` as the `document_id` on the verify call to resolve the outstanding review.
* **Track the outcome** — check `verification_status` after the document verification to confirm the customer moved to `ACCEPTED`.
## Related guides
Understand the initial verification that document verification steps up.
Onboard the customer to be verified.
Capture the KYC data collection disclosure.
Open accounts once the customer is verified.
## API reference
* [Start a document verification session](/v2/reference/docvsession)
* [Run a verification](/v2/reference/verify)
* [Create a person](/v2/reference/createperson)
* [Create a disclosure](/v2/reference/createdisclosure)
# Domestic Wires
Source: https://docs.synctera.com/v2/docs/domestic-wires
Domestic wires refer to the transfer of funds between 2 US based accounts.
## Domestic Wires Payments
### Overview
This type of transaction is used by individuals and businesses for making payments, transferring funds between accounts, or conducting other financial transactions within the country's borders.
The primary systems used to process domestic wire transfers is the Fedwire payment system.
### What is the Fedwire Payment System?
The Fedwire payment system is a real-time gross settlement system operated by the Federal Reserve Bank. It provides a secure and efficient means of transferring funds between banks and other eligible financial institutions within the country. The Fedwire payment system is generally used to make large-value, time-critical payments.
To initiate a domestic wire transfer through the Fedwire system, the sender's bank needs to have access to the system and comply with its rules and regulations. The sender provides the necessary details, such as the recipient's bank account number, routing number, and the amount to be transferred.
The Fedwire system ensures the immediate and final transfer of funds from the sender's bank to the recipient's bank. The transaction is processed in real-time, which means the funds are settled immediately upon receipt.
### What are the benefits of sending domestic wire payments?
The Fedwire payment system offers several advantages:
1. Speed: Domestic wire transfers through the Fedwire system are processed in real-time, which means that transactions are settled immediately upon receipt.
2. Security: The Fedwire system employs robust security measures to protect the integrity and confidentiality of the transactions, ensuring the safe transfer of funds.
3. Finality: Once a transaction is processed through the Fedwire system, it is considered final and irrevocable. This provides certainty to both the sender and the recipient that the funds will be transferred as intended.
4. High Limits: The Fedwire system has high transaction limits, allowing for the transfer of large sums of money within the domestic banking system.
5. Direct Bank-to-Bank Transfer: With the Fedwire system, the funds are transferred directly between banks, eliminating the need for intermediaries and reducing the associated costs.
### How do I send a domestic Wire payment?
In order to send a domestic wire you must create a customer and account to represent the sender/originator of the wire who is a direct customer of your fintech. You will also need to create a customer and account for any external recipients that your customers plan to send wires to.
1.1 **Create a Customer**
In order for the originator to transact on the Synctera platform, that person/business must be stored as a customer in our system. Part of that includes completing the KYC/KYB process. See [Create a Personal Customer](/v2/docs/create-a-personal-customer) or [Create a Business Customer](/v2/docs/create-a-business) for details.
1.2 **Create an Account**
Create an account for the customer your created in step 1.1. [Create an Account](/v2/docs/create-accounts-guide)
2.1 **Create a “non-customer” Customer**
Even though the beneficiary is not a customer of your fintech, that person/business must also be represented as a customer in the Synctera system. See [Create a Personal Customer](/v2/docs/create-a-personal-customer) or [Create a Business Customer](/v2/docs/create-a-business) for details.
> NOTE: Make sure you set the `is_customer` flag to `false` to indicate that the individual is not a direct customer of your fintech.
2.2 **Create an External Account**
Create an external account for the customer you created in step 2.1. Unlike some of our other payment rails, you do not need to link the external account via Plaid or Finiticy since the external account owner is not a direct customer of your fintech. For details skip to Step 3 in the [Create an External Account](/v2/docs/external-accounts-guide) guide.
> NOTE: Be sure to provide a `wire_routing_number` as the `routing_identifier`
3.1 **Fund the Account**
The last step that must be completed before a customer can send a wire is to deposit funds into the originating account. This can be done by originating an ACH debit to an external account that is associated with the customer. See the [ACH Guide](/v2/docs/ach-guide) for steps on how to fund an account via ACH.
4.1 **Send a Wire Request**
Once the previous steps have been completed, a wire payment can be sent from the originator account to the beneficiary account. Take a look at the [Wires API reference](/v2/reference/createwire) for an explanation of what to include in the request.
### What do I need to do to receive a Wire transaction?
Nothing. No action is required. Synctera will process all credits for any Wire transactions that we receive on behalf of you or your customers. Check your Synctera dashboard to view all Wire transactions that have been sent or received. Synctera also supports webhooks to notify you of all Wire transactions.
# Enhanced Due Diligence
Source: https://docs.synctera.com/v2/docs/enhanced-due-diligence-guide
Enhanced due diligence (EDD) lets you submit additional information about high-risk customers, businesses, accounts, and transactions to support anti-money-laundering review and Customer Risk Rating.
## Overview
Enhanced due diligence (EDD) is the process financial institutions use to check and monitor high-risk customers and unusual transactions for potential money-laundering activity. The information required varies by scenario, so Synctera's EDD API accepts a different set of fields for each type of resource.
**An EDD submission** attaches extra information to an existing resource. Combined with the data gathered during KYC/KYB, it feeds Synctera's [Customer Risk Rating (CRR)](/v2/reference/listcrr) and supports responses to requests for information (RFIs).
Key characteristics:
* **Typed by resource** — each submission targets a `CUSTOMER`, `BUSINESS`, `ACCOUNT`, or `TRANSACTION`, and the accepted fields differ per type.
* **Reason-driven** — the `reason` field (`CASE_REQUEST` or `STANDARD_SUBMISSION`) records why the EDD was created; a `CASE_REQUEST` also requires a `case_id`.
* **CRR input** — customer and business EDD, together with KYC/KYB data, lets Synctera compute a Customer Risk Rating.
* **Attachable** — supporting documents can be linked to an EDD using the [Documents API](/v2/reference/createdocument).
### When to submit EDD
* **During onboarding** — submit additional information right after creating a customer or business.
* **In response to an RFI** — answer a request for information on a KYC/KYB, account, or transaction case, referencing the `case_id`.
* **Recurring updates** — periodically refresh a customer's or business's information.
* **High CRR outcome** — when a customer or business is rated **High** by the CRR, EDD is required.
## Prerequisites
This guide assumes you have:
* Created a [personal customer](/v2/docs/create-a-personal-customer) or [business customer](/v2/docs/create-a-business)
* An existing account or transaction (for account or transaction EDD)
You should also be familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
## The EDD object
Every EDD submission shares a common set of fields; the remaining fields depend on `related_resource_type`.
| Field | Description |
| ----------------------- | --------------------------------------------------------------------------------- |
| `related_resource_id` | **Required.** The `id` of the customer, business, account, or transaction. |
| `related_resource_type` | **Required.** One of `CUSTOMER`, `BUSINESS`, `ACCOUNT`, `TRANSACTION`. |
| `reason` | **Required.** Why the EDD was created: `CASE_REQUEST` or `STANDARD_SUBMISSION`. |
| `case_id` | Required when `reason` is `CASE_REQUEST`; the case the submission responds to. |
| `additional_questions` | Free-form question/answer pairs, each with a `section`, `question`, and `answer`. |
Submit an EDD with [POST /v2/edd](/v2/reference/createedd). The sections below show the additional fields for each resource type.
## Submitting EDD
### Customer EDD
Customer EDD submits additional information about a personal customer, either after creation or as a response to an RFI on a KYC case. Along with KYC data, it lets Synctera run a Customer Risk Rating.
| Field | Description |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `employment_type` | `CONTRACTOR`, `FULL_TIME`, `INTERN`, `PART_TIME`, `VOLUNTEER`, `TEMPORARY`, `RETIRED`, `STUDENT`, or `OTHER`. |
| `income` | Employment income: `amount`, `currency`, `frequency`, and `source`. |
| `occupation` | Free-text occupation (e.g. Product Manager). |
| `occupation_industry` | Industry classification (e.g. `FINANCE_AND_INSURANCE`, `CONSTRUCTION`, `RETAIL_TRADE`). See the [API reference](/v2/reference/createedd) for the full enum. |
| `residence_type` | `OWN`, `RENT`, or `OTHER`. |
| `residential_expense` | Residential expenses: `amount`, `currency`, and `frequency`. |
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/edd \
--data-binary '
{
"related_resource_id": "{PERSON_ID}",
"related_resource_type": "CUSTOMER",
"reason": "CASE_REQUEST",
"case_id": "12345",
"employment_type": "FULL_TIME",
"occupation": "Investment Banker",
"occupation_industry": "FINANCE_AND_INSURANCE",
"residence_type": "RENT",
"residential_expense": {
"amount": 1000000,
"currency": "USD",
"frequency": "MONTHLY"
},
"income": {
"amount": 8000000,
"currency": "USD",
"frequency": "MONTHLY",
"source": "EMPLOYMENT"
},
"additional_questions": [
{
"section": "income",
"question": "How can you make so much money?",
"answer": "Buy low, sell high"
},
{
"section": "occupation",
"question": "If employed, how long have you held your current position?",
"answer": "Since 2023-04-15"
}
]
}'
```
### Business EDD
Business EDD submits additional information about a business, either after creation or as a response to an RFI on a KYB case. Along with KYB data, it lets Synctera run a Customer Risk Rating.
| Field | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `country` | The business's headquarters country. |
| `industry_type` | The business's industry (e.g. `MINING_QUARRYING_AND_OIL_AND_GAS_EXTRACTION`). See the [API reference](/v2/reference/createedd) for the full enum. |
| `estimated_revenue` | Estimated revenue: `amount`, `currency`, and `frequency`. |
| `specific_involvement` | A complete set of boolean declarations (e.g. `cannabis_industry`, `licensed_money_service_business`, `arms_defense_or_security`). All flags must be provided. |
| `transaction_volume` | Expected transaction volume on (`on_synctera: true`) and off (`on_synctera: false`) the Synctera platform. |
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/edd \
--data-binary '
{
"related_resource_id": "c10ec16e-9d92-49af-892d-0c3627e0926e",
"related_resource_type": "BUSINESS",
"reason": "STANDARD_SUBMISSION",
"country": "NO",
"industry_type": "MINING_QUARRYING_AND_OIL_AND_GAS_EXTRACTION",
"specific_involvement": {
"ngo_or_non_profit": false,
"licensed_money_service_business": false,
"cannabis_industry": false,
"wellness_gift_cards_wholesaler": false,
"law_firm_or_fiduciary": false,
"arms_defense_or_security": false,
"precious_metals_stones_or_jewellery": false,
"embassies_or_consulates": false,
"illegal_activities_prostitution_or_pirated_goods": false,
"lotteries_crypto_currency_or_illegal_internet_gambling": false,
"shell_banks_or_unlicensed_money_service_business": false
},
"estimated_revenue": {
"amount": 50000000,
"currency": "USD",
"frequency": "MONTHLY"
},
"transaction_volume": [
{
"on_synctera": true,
"channel": "WIRES",
"channel_coverage": "INTERNATIONAL",
"currency": "USD",
"transaction_count": 10,
"amount": 8000000,
"frequency": "MONTHLY"
},
{
"on_synctera": true,
"channel": "ACH",
"channel_coverage": "DOMESTIC",
"currency": "USD",
"transaction_count": 50,
"amount": 1000000,
"frequency": "MONTHLY"
}
],
"additional_questions": [
{
"section": "business",
"question": "Is the business involved in upstream or downstream?",
"answer": "Upstream. We mainly provide geological and research services"
},
{
"section": "revenue",
"question": "What are some of the main business expenses?",
"answer": "Salaries, travel expenses, and research"
}
]
}'
```
### Account EDD
Account EDD submits additional information about an account, typically in response to an account under review or frozen. Beyond the common fields, it accepts `additional_questions`.
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/edd \
--data-binary '
{
"related_resource_id": "c10ec16e-9d92-49af-892d-0c3627e0926e",
"related_resource_type": "ACCOUNT",
"reason": "CASE_REQUEST",
"case_id": "12345",
"additional_questions": [
{
"section": "reason",
"question": "Explain the reason for the high number of transactions with value more than $25,000.",
"answer": "These were invoice payments."
}
]
}'
```
### Transaction EDD
Transaction EDD submits additional information about a specific transaction, typically in response to an RFI for a transaction in review.
| Field | Description |
| --------------------- | -------------------------------------------- |
| `transaction_purpose` | The purpose of the transaction. |
| `source_of_funds` | `INVESTMENT`, `SALARY`, `SALES`, or `OTHER`. |
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/edd \
--data-binary '
{
"related_resource_id": "5c96bda6-60d9-4d6c-b05f-6ca4bcce79ce",
"related_resource_type": "TRANSACTION",
"reason": "CASE_REQUEST",
"case_id": "12345",
"source_of_funds": "OTHER",
"transaction_purpose": "for tuition payment",
"additional_questions": [
{
"section": "Source of funds",
"question": "Where did these funds originate from?",
"answer": "A recent estate disbursement."
},
{
"section": "Transaction purpose",
"question": "What was this payment for?",
"answer": "Tuition fees at University of New York"
}
]
}'
```
## Best practices
When `reason` is `CASE_REQUEST`, always include the `case_id`. Omitting it means the submission cannot be tied back to the case that requested it.
* **Submit the complete field set** — for business EDD, every `specific_involvement` flag must be provided, even when `false`.
* **Attach supporting documents** — link source documents to the EDD with the [Documents API](/v2/reference/createdocument) rather than pasting them into answers.
* **Keep information current** — submit recurring updates for higher-risk customers and businesses so the CRR reflects the latest data.
* **Answer RFIs specifically** — use `additional_questions` to give clear, reviewable answers a compliance analyst can act on.
## Related guides
Onboard a person before submitting customer EDD.
Onboard a business before submitting business EDD.
EDD complements the data collected during verification.
## API reference
* [Create an EDD](/v2/reference/createedd)
* [List EDDs](/v2/reference/listedd)
* [Get an EDD](/v2/reference/getedd)
* [Delete an EDD](/v2/reference/deleteedd)
* [Create a document](/v2/reference/createdocument)
# Evaluation Overrides
Source: https://docs.synctera.com/v2/docs/evaluation-override
Evaluation overrides let you temporarily bypass fraud rules or spend control limits for a specific customer, account, card, or transaction.
## Overview
Synctera's platform evaluates every transaction against a set of risk rules — fraud checks and [spend controls](/v2/docs/spend-controls-guide). In most cases these automated evaluations produce the right outcome, but there are situations where a human reviewer determines that a decline or case was a false positive and the customer should be allowed to proceed.
**Evaluation overrides** give you a controlled way to do this. An override tells the platform: *"for this customer (and optionally this account, card, or spend control), skip the specified evaluation for a defined period of time."*
Key characteristics:
* **Scoped** — each override is tied to a `customer_id` and can optionally be narrowed to a specific `account_id`, `card_id`, `spend_control_id`, or `transaction_id`.
* **Typed** — the `type` field indicates which category of evaluation to bypass: `FRAUD` (fraud-rule evaluation) or `SPEND_CONTROL` (spend-control limit evaluation).
* **Time-bound** — every override has an `active_at` timestamp and an optional `expires_at` timestamp so overrides don't remain in effect indefinitely.
* **Auditable** — a `reason` (minimum 3 characters) is required on creation, providing a clear audit trail for compliance review.
### When to use evaluation overrides
Common scenarios include:
* **False-positive fraud decline** — A customer contacts support because a legitimate transaction was blocked by a fraud rule. After review, an agent creates a `FRAUD` override so the customer can retry.
* **Temporary spend-control exception** — A customer needs to make a one-time large purchase that exceeds their normal spend-control limit. A `SPEND_CONTROL` override allows the transaction without permanently changing the spend control.
* **Transaction-specific bypass** — An override scoped to a `transaction_id` allows a single previously-declined transaction to be retried.
## Prerequisites
This guide assumes you have:
* Created a [personal customer](/v2/docs/create-a-personal-customer) or [business customer](/v2/docs/create-a-business)
* Familiarity with [spend controls](/v2/docs/spend-controls-guide) (if creating `SPEND_CONTROL` overrides)
You should also be familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
## The evaluation override object
An evaluation override contains the following key fields:
| Field | Description |
| ------------------ | ---------------------------------------------------------------- |
| `id` | Unique identifier (read-only, assigned on creation). |
| `customer_id` | **Required.** The customer the override applies to. |
| `type` | The evaluation category to override: `FRAUD` or `SPEND_CONTROL`. |
| `reason` | **Required.** Why the override was created (min 3 characters). |
| `active_at` | When the override takes effect. |
| `expires_at` | When the override expires (optional). |
| `account_id` | Optionally scope the override to a specific account. |
| `card_id` | Optionally scope the override to a specific card. |
| `spend_control_id` | Optionally scope the override to a specific spend control. |
| `transaction_id` | Optionally scope the override to a specific transaction. |
```json theme={"system"}
{
"id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"customer_id": "5f4ff599-7c29-4f69-a3d9-e103e151afbd",
"account_id": "30044785-ddb0-4a51-be1c-402bd4ba2b2b",
"type": "FRAUD",
"reason": "False positive - customer confirmed transaction is legitimate.",
"active_at": "2024-01-01T00:00:00.000Z",
"expires_at": "2024-12-31T23:59:59.000Z",
"creation_time": "2024-01-01T00:00:00.000Z",
"last_updated_time": "2024-01-01T00:00:00.000Z"
}
```
See the [API reference](/v2/reference/listevaluationoverrides) for the full request and response schemas.
## Managing evaluation overrides
Use [POST /v2/evaluation\_overrides](/v2/reference/createevaluationoverride). At minimum you must provide a `customer_id` and a `reason`.
**Example: Fraud override for a customer**
After reviewing a fraud case, an agent determines the decline was a false positive and creates an override so the customer can transact normally for the next 24 hours:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/evaluation_overrides \
--data-binary '
{
"customer_id": "5f4ff599-7c29-4f69-a3d9-e103e151afbd",
"type": "FRAUD",
"reason": "False positive - customer confirmed transaction is legitimate.",
"active_at": "2024-06-01T00:00:00.000Z",
"expires_at": "2024-06-02T00:00:00.000Z"
}'
```
**Example: Spend-control override for a specific account and spend control**
A customer needs to make a one-time purchase that exceeds their weekly card limit. The override is scoped to their specific account and spend control:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/evaluation_overrides \
--data-binary '
{
"customer_id": "5f4ff599-7c29-4f69-a3d9-e103e151afbd",
"account_id": "30044785-ddb0-4a51-be1c-402bd4ba2b2b",
"spend_control_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"type": "SPEND_CONTROL",
"reason": "One-time exception for large appliance purchase.",
"active_at": "2024-06-01T00:00:00.000Z",
"expires_at": "2024-06-01T23:59:59.000Z"
}'
```
The response includes the system-generated `id`, `creation_time`, and `last_updated_time`:
```json theme={"system"}
{
"id": "74454076-c36b-44fb-8062-d1743d668236",
"customer_id": "5f4ff599-7c29-4f69-a3d9-e103e151afbd",
"account_id": "30044785-ddb0-4a51-be1c-402bd4ba2b2b",
"spend_control_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"type": "SPEND_CONTROL",
"reason": "One-time exception for large appliance purchase.",
"active_at": "2024-06-01T00:00:00.000Z",
"expires_at": "2024-06-01T23:59:59.000Z",
"creation_time": "2024-05-31T14:22:08.123Z",
"last_updated_time": "2024-05-31T14:22:08.123Z"
}
```
Use [GET /v2/evaluation\_overrides](/v2/reference/listevaluationoverrides) to list overrides. You can filter by customer, account, card, spend control, transaction, type, or reason.
```shell theme={"system"}
curl \
-X GET \
-H "Authorization: Bearer $apikey" \
"https://api.synctera.com/v2/evaluation_overrides?customer_id=5f4ff599-7c29-4f69-a3d9-e103e151afbd&type=FRAUD"
```
To retrieve a single override by ID, use [GET /v2/evaluation\_overrides/\{evaluation\_override\_id}](/v2/reference/getevaluationoverride):
```shell theme={"system"}
curl \
-X GET \
-H "Authorization: Bearer $apikey" \
"https://api.synctera.com/v2/evaluation_overrides/74454076-c36b-44fb-8062-d1743d668236"
```
Use [PATCH /v2/evaluation\_overrides/\{evaluation\_override\_id}](/v2/reference/updateevaluationoverride) to modify an existing override. You can update the `active_at`, `expires_at`, and `reason` fields.
For example, to extend an override's expiration:
```shell theme={"system"}
curl \
-X PATCH \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/evaluation_overrides/74454076-c36b-44fb-8062-d1743d668236 \
--data-binary '
{
"expires_at": "2024-06-07T23:59:59.000Z",
"reason": "Extended override - customer traveling internationally this week."
}'
```
To remove an override immediately (rather than waiting for it to expire), use [DELETE /v2/evaluation\_overrides/\{evaluation\_override\_id}](/v2/reference/deleteevaluationoverride):
```shell theme={"system"}
curl \
-X DELETE \
-H "Authorization: Bearer $apikey" \
"https://api.synctera.com/v2/evaluation_overrides/74454076-c36b-44fb-8062-d1743d668236"
```
## Best practices
Evaluation overrides bypass safety controls. Use them judiciously and always with a clear, documented reason.
* **Set an expiration** — Always provide an `expires_at` value so overrides don't persist longer than intended. Prefer the shortest window that covers the customer's need.
* **Scope narrowly** — Use `account_id`, `card_id`, `spend_control_id`, or `transaction_id` to limit the override to only what's necessary rather than giving a blanket bypass for the customer.
* **Document the reason** — Write a clear, specific `reason` that a compliance reviewer can understand later (e.g., *"False positive — customer confirmed wire to known payee"* rather than *"override"*).
* **Review regularly** — Use the list endpoint with filters to audit active overrides and ensure none have been left in place longer than needed.
* **Prefer deletion over expiry for immediate revocation** — If circumstances change and the override is no longer appropriate, delete it rather than waiting for it to expire.
## API reference
See the full Evaluation Overrides API reference for request/response schemas and all available parameters:
* [List evaluation overrides](/v2/reference/listevaluationoverrides)
* [Create an evaluation override](/v2/reference/createevaluationoverride)
* [Get an evaluation override](/v2/reference/getevaluationoverride)
* [Update an evaluation override](/v2/reference/updateevaluationoverride)
* [Delete an evaluation override](/v2/reference/deleteevaluationoverride)
# External Accounts
Source: https://docs.synctera.com/v2/docs/external-accounts-guide
External accounts represent a customer's accounts at other financial institutions. Verify ownership through Plaid or Finicity, then check balances and transactions or move money via ACH.
## Overview
An external account is a customer's account at another financial institution — checking, savings, credit card, or investment. To use one, you must **verify ownership**. Synctera has partnered with Plaid and Finicity to verify ownership and retrieve balance and transaction information.
**Verifying an external account** unlocks capabilities such as balance checks before issuing ACH debits (to reduce overdrafts) and account-owner validation. You can originate ACH credits without verifying the owner, but ACH debits require the customer to have verified that they own the external account.
Key characteristics:
* **Ownership-verified** — accounts are linked through Plaid Link or Finicity Connect, which confirm the customer controls the account.
* **Vendor-backed** — link through `PLAID` or `FINICITY`; each has slightly different flows.
* **Actionable** — once added, you can list transactions and fetch balances (except for micro-deposit links).
* **Manageable** — accounts can be listed, viewed, updated (for manual accounts), synced, and deleted.
If you use another provider such as MX, you can create external accounts directly by posting to the `/v2/external_accounts` API. Synctera confirms that customers validate ownership of the external account as part of the launch process.
## Prerequisites
This guide assumes you have:
* Created a [personal customer](/v2/docs/create-a-personal-customer) or [business customer](/v2/docs/create-a-business)
You should also be familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
Throughout, provide `customer_id` for personal customers or `business_id` for business customers. If you have not created a customer yet, do so first via the [Create a Person](/v2/reference/createperson).
**Plaid vs Finicity.** Both provide similar services; the main difference is account linking. Plaid Link requires you to integrate Plaid's SDKs directly. Finicity Connect is a web interface hosted by Finicity where users enter their account credentials. Note that **micro-deposits are only supported by Plaid**.
## Verification steps (Plaid)
A link token is the first step to verifying an external account; it grants access to Plaid Link, the UI where customers provide their bank details. Integrate [Plaid Link](https://plaid.com/docs/link/) ([Android](https://plaid.com/docs/link/android/), [iOS](https://plaid.com/docs/link/ios/), [WebView](https://plaid.com/docs/link/webview/), [React Native](https://plaid.com/docs/link/react-native/), [web](https://plaid.com/docs/link/web/)), or use our [test page](https://app.synctera.com/widgets/plaid-link) to try calls before integrating.
Request a link token with [Create Link Token](/v2/reference/createverificationlinktoken):
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/link_tokens \
--data-binary '
{
"client_name": "Name of your application",
"customer_id": "27b1bfd0-d83f-42bc-9b2a-a0241adcba9a",
"country_codes": ["US"],
"type": "DEPOSITORY",
"language": "EN"
}'
```
The `type` of link token determines what kind of account can be verified:
| Type | Use |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DEPOSITORY` | Checking and savings accounts. |
| `MICRO_DEPOSIT` | Depository accounts verified via micro-deposits — for institutions without instant verification, or when the customer prefers not to share login credentials. |
| `CREDIT` | Credit card accounts. |
| `INVESTMENT` | Investment accounts. |
```json theme={"system"}
{
"client_name": "Name of your application",
"country_codes": ["US"],
"customer_id": "27b1bfd0-d83f-42bc-9b2a-a0241adcba9a",
"expiration": "2022-03-08T21:29:13Z",
"language": "EN",
"link_token": "link-sandbox-b68c1a6f-c514-4535-9781-0c2b8e2281ad",
"request_id": "Roius8bd2DeXq6D",
"type": "DEPOSITORY",
"verify_owner": false
}
```
Use the `link_token` to launch Plaid Link. When the customer confirms their account, Plaid sends an [onSuccess callback](https://plaid.com/docs/link/web/#onsuccess) with account metadata and a `public_token` — the input for the next step. (On the test page, enter the `link_token`, click **Link Account**, and the `public_token` appears at the top.)
The access token holds the details Plaid retrieved and lets you manage the account through Synctera. Create one with [Access Token](/v2/reference/createaccesstoken). For all link token types **except** `MICRO_DEPOSIT`, Plaid's success response includes `vendor_public_token` and `vendor_institution_id`, which must be included here.
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/access_tokens \
--data-binary '
{
"customer_id": "27b1bfd0-d83f-42bc-9b2a-a0241adcba9a",
"vendor_institution_id": "ins_3",
"vendor_public_token": "public-sandbox-45524484-45f9-490e-bb44-aec4131f6fb8"
}'
```
```json theme={"system"}
{
"customer_id": "17f4c82d-a398-48ed-a9c8-5bd9a53fe862",
"request_id": "xsHszMQwyB4IbeP",
"vendor_access_token": "access-sandbox-2fa1d335-2d85-4563-8faa-d5e2a022d4db",
"vendor_customer_id": "17f4c82d-a398-48ed-a9c8-5bd9a53fe862",
"vendor_institution_id": "ins_3",
"vendor_public_token": "public-sandbox-46a9e08e-a153-4189-85ac-143f8061746b"
}
```
The response returns the `vendor_access_token`; verification is complete. For the micro-deposit variant, see [Micro-deposits flow](#micro-deposits-flow).
Persist the `vendor_access_token` and add the accounts the customer selected with [Add External Accounts](/v2/reference/addvendorexternalaccounts). Include:
* `customer_id` or `business_id` — the customer associated with these accounts.
* `customer_type` — `PERSONAL` or `BUSINESS`.
* `vendor` — `PLAID` for this flow.
* `vendor_access_token` — from the previous step.
* `vendor_account_ids` — the vendor account IDs the customer chose to link (Plaid `account_id`s).
* `verify_owner` — whether to verify account-owner info against Synctera records. Not supported for business customers or the micro-deposit flow.
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/add_vendor_accounts \
--data-binary '
{
"customer_id": "3bd1a112-a98e-49ab-831c-33278cab49fb",
"customer_type": "PERSONAL",
"vendor": "PLAID",
"vendor_access_token": "access-sandbox-de3ce8ef-33f8-452c-a685-8671031fc0f6",
"vendor_account_ids": ["blgvvBlXw3cq5GMPwqB6s6q4dLKB9WcVqGDGo"],
"verify_owner": false
}'
```
The response lists both added and failed accounts, so check which succeeded:
```json theme={"system"}
{
"added_accounts": [
{
"id": "df0978ca-9816-4b3f-b3c1-9f1354e062fe",
"customer_id": "1381e689-90ff-4176-8f65-a4a1225120ff",
"nickname": "Home Loan Account",
"type": "CHECKING",
"status": "ACTIVE",
"account_identifiers": {
"iban": "LB7750793201TP7049N596464T98",
"number": "78277121"
},
"account_owner_names": ["Jay Hansen"],
"routing_identifiers": {
"ach_routing_number": "756392185",
"bank_countries": ["US"],
"bank_name": "Chase",
"eft_routing_number": "02101140",
"swift_code": "CHASUS3AXXX",
"wire_routing_number": "756392185"
},
"verification": {
"status": "VERIFIED",
"vendor": "PLAID"
}
}
],
"failed_accounts": [
{
"reason": "FAILED_VERIFICATION",
"reason_description": "string",
"vendor_account_id": "blgvvBlXw3cq5GMPwqB6s6q4dLKB9WcVqGDGo",
"vendor_error_message": "string",
"vendor_request_id": "string"
}
]
}
```
You can also retrieve all linked accounts with [List External Accounts](/v2/reference/listexternalaccounts).
### Micro-deposits flow
Micro-deposits verify a depository account by depositing \$0.01 whose transaction description contains a code the customer must confirm. This flow assumes you have already created a `MICRO_DEPOSIT` link token and launched Plaid Link (choosing **Manual**, or **Link With Account Numbers**), entered the account information, and received a `public_token`.
Create an access token as usual, but because institution information is not returned in the micro-deposit flow, set `vendor_institution_id` to `"-"`:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/access_tokens \
--data-binary '
{
"customer_id": "27b1bfd0-d83f-42bc-9b2a-a0241adcba9a",
"vendor_institution_id": "-",
"vendor_public_token": "public-sandbox-45524484-45f9-490e-bb44-aec4131f6fb8"
}'
```
The response returns the `vendor_access_token`. Wait up to 48 hours for the micro-deposit to arrive (or use the [test data](/v2/docs/sandbox-test-cases#micro-deposit-testing-scenarios) to proceed immediately).
Create another link token, this time including the `vendor_access_token`. This secondary token lets the customer enter the code from the micro-deposit's transaction description via Plaid Link.
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/link_tokens \
--data-binary '
{
"type": "MICRO_DEPOSIT",
"language": "EN",
"client_name": "Name of your application",
"country_codes": ["US"],
"customer_id": "27b1bfd0-d83f-42bc-9b2a-a0241adcba9a",
"vendor_access_token": "access-sandbox-bf7436d8-8a18-4a11-a293-66cee2355eb6"
}'
```
Launch Plaid Link with this token and prompt the customer to submit the 3-letter code from the micro-deposit description.
Once the code is submitted, verification is complete. Add the account as in the standard [Add the verified external accounts](#add-the-verified-external-accounts) step.
### Update mode
Update mode lets you re-authenticate a disconnected account or link additional accounts at the same bank. Create a new link token that includes both `vendor_institution_id` and `vendor_access_token`:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/link_tokens \
--data-binary '
{
"type": "DEPOSITORY",
"language": "EN",
"client_name": "Synctera",
"country_codes": ["US"],
"customer_id": "8bb5f852-5b53-4b0e-97f0-43bcb4f8532b",
"vendor_institution_id": "ins_56",
"vendor_access_token": "access-sandbox-23ac17ff-b4b7-4dce-8194-2e17f9656ccb"
}'
```
Launch Plaid Link with the returned token: already-linked accounts appear in the list, and the customer can add or remove accounts. After submitting, you receive a new access token — use it to persist the changes with [Sync Vendor Accounts](/v2/reference/syncvendorexternalaccounts).
## Verification steps (Finicity)
The Connect URL opens Finicity's hosted web interface where customers link their accounts. Request it with [Create Link Token](/v2/reference/createverificationlinktoken):
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/link_tokens \
--data-binary '
{
"client_name": "Name of your application",
"customer_id": "27b1bfd0-d83f-42bc-9b2a-a0241adcba9a",
"country_codes": ["US"],
"type": "DEPOSITORY",
"language": "EN"
}'
```
Finicity supports `DEPOSITORY`, `CREDIT`, and `INVESTMENT` link tokens (no micro-deposits).
```json theme={"system"}
{
"client_name": "Name of your application",
"country_codes": ["US"],
"customer_id": "27b1bfd0-d83f-42bc-9b2a-a0241adcba9a",
"expiration": "2022-03-08T21:29:13Z",
"language": "EN",
"link_token": "https://connect2.finicity.com?customerId=6003202401&experience=8ab62f1c-5f9b-4f3f-a20f-377aa28a8de2&origin=url&partnerId=2445583932772&signature=bdc6b061a8b25822de164fc857a74601c1c1f1b816d9200a8d14dd57c1095246×tamp=1656025350309&ttl=1656032550309",
"request_id": "Roius8bd2DeXq6D",
"type": "DEPOSITORY",
"verify_owner": false
}
```
Save the vendor customer ID for later — it is the `customerId` query parameter in the URL (here, `6003202401`).
Load the returned URL so the customer can search for their financial institution, provide credentials, and link their accounts.
Persist the linked accounts with [Sync External Accounts](/v2/reference/syncvendorexternalaccounts). Include:
* `customer_id` or `business_id` — the customer associated with these accounts.
* `customer_type` — `PERSONAL` or `BUSINESS`.
* `vendor` — `FINICITY` for this flow.
* `vendor_customer_id` — the customer ID returned by the vendor.
* `verify_owner` — not supported for business customers.
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/add_vendor_accounts \
--data-binary '
{
"customer_id": "3bd1a112-a98e-49ab-831c-33278cab49fb",
"customer_type": "PERSONAL",
"vendor": "FINICITY",
"vendor_customer_id": "6003202403",
"verify_owner": false
}'
```
All accounts selected in Finicity Connect are added as verified external accounts. Re-run the call after linking or unlinking accounts to add or remove them.
The Finicity flow only supports adding accounts through [Sync External Accounts](/v2/reference/syncvendorexternalaccounts), not through [Add External Accounts](/v2/reference/addvendorexternalaccounts).
## Operations
Once an account is linked and added, you can list transactions and fetch balances. These operations are **not** available for micro-deposit links, since Plaid does not allow Auth on micro-deposits.
### List transactions
Retrieve transactions for an external account with [List Transactions](/v2/reference/getexternalaccounttransactions). `start_date` is inclusive and `end_date` is exclusive — for all transactions on 2022-01-01, use `start_date=2022-01-01&end_date=2022-01-02`.
```shell theme={"system"}
curl \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
"https://api.synctera.com/v2/external_accounts/df0978ca-9816-4b3f-b3c1-9f1354e062fe/transactions?start_date=2022-02-01&end_date=2022-02-02"
```
```json theme={"system"}
{
"transactions": [
{
"amount": 0,
"currency": "USD",
"category": ["string"],
"check_number": "string",
"is_pending": true,
"date": "2022-02-01",
"authorized_date": "2022-02-01",
"merchant_name": "Fancy store",
"payment_channel": "IN_STORE",
"payment_method": "string",
"transaction_id": "string"
}
]
}
```
### Get balance
Fetch an account balance by `external_account_id`. The response reports `available` and `current` balances in cents (1000 = \$10.00), the ISO 4217 `currency`, and the balance `limit`.
```shell theme={"system"}
curl \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/df0978ca-9816-4b3f-b3c1-9f1354e062fe/balance
```
```json theme={"system"}
{
"available": 10000,
"current": 11000,
"currency": "SEK",
"limit": 20000
}
```
## Account management
### List external accounts
View all external accounts, optionally filtered by `customer_id` or `business_id`, with [List External Accounts](/v2/reference/listexternalaccounts):
```shell theme={"system"}
curl \
-H "Authorization: Bearer $apikey" \
https://api.synctera.com/v2/external_accounts
```
```json theme={"system"}
{
"external_accounts": [
{
"id": "1542ea0f-09ef-410c-9d66-57f36af5b52b",
"customer_id": "17f4c82d-a398-48ed-a9c8-5bd9a53fe862",
"nickname": "Plaid Checking",
"type": "CHECKING",
"status": "ACTIVE",
"account_identifiers": { "number": "0000" },
"routing_identifiers": {
"ach_routing_number": "1533",
"bank_countries": ["US"],
"bank_name": "Chase"
},
"vendor_data": { "account_number_mask": "0000", "institution_id": "ins_3" },
"verification": { "status": "VERIFIED", "vendor": "PLAID" }
}
]
}
```
### View a specific external account
Fetch a single account by ID with [Get External Account](/v2/reference/getexternalaccount):
```shell theme={"system"}
curl \
-H "Authorization: Bearer $apikey" \
https://api.synctera.com/v2/external_accounts/1542ea0f-09ef-410c-9d66-57f36af5b52b
```
### Sync vendor accounts
After running Plaid Link in [update mode](#update-mode), persist the changes (adds the newly linked accounts and deletes the unlinked ones) with [Sync Vendor Accounts](/v2/reference/syncvendorexternalaccounts). Owner-identity verification is not supported for business customers — omit `verify_owner` or set it to `false`.
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/sync_vendor_accounts \
--data-binary '
{
"customer_id": "0d36a87d-a3dc-4ab9-8732-6c05e955c7fa",
"customer_type": "PERSONAL",
"vendor": "PLAID",
"vendor_access_token": "access-sandbox-de3ce8ef-33f8-452c-a685-8671031fc0f6",
"vendor_account_ids": ["blgvvBlXw3cq5GMPwqB6s6q4dLKB9WcVqGDGo"],
"verify_owner": false
}'
```
The response contains `added_accounts`, `deleted_accounts`, and `failed_accounts`.
### Unlink an external account
Deleting an account from Synctera does **not** unlink it from Plaid. To fully unlink, follow this section before deleting.
Create a link token in [update mode](#update-mode) (include `vendor_institution_id`), launch Plaid Link, uncheck the accounts to unlink, and click Continue. The unlinked accounts can then be [deleted from Synctera](#delete-an-external-account). If you linked new accounts, persist them with [Sync Vendor Accounts](#sync-vendor-accounts).
### Delete an external account
Delete an account from Synctera with [Delete External Account](/v2/reference/deleteexternalaccount). If it is still linked at Plaid, you can add it back.
```shell theme={"system"}
curl \
-X DELETE \
-H "Authorization: Bearer $apikey" \
https://api.synctera.com/v2/external_accounts/1542ea0f-09ef-410c-9d66-57f36af5b52b
```
```json theme={"system"}
{
"id": "1542ea0f-09ef-410c-9d66-57f36af5b52b",
"resource": "external_account"
}
```
### Create an external account manually
If you verify account ownership outside Synctera's API, create the external account manually with [Add an External Account](/v2/reference/addexternalaccounts):
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts \
--data-binary '
{
"type": "CHECKING",
"customer_id": "17f4c82d-a398-48ed-a9c8-5bd9a53fe862",
"customer_type": "PERSONAL",
"account_official_name": "Primary checking",
"vendor_account_id": "m1w75KWEQQh57qaZlo6mIGVR5r64vDSLP74dr",
"account_owner_names": ["Sponge Bob"],
"routing_identifiers": {
"bank_name": "Bank of Orion",
"bank_countries": ["US"],
"ach_routing_number": "110001001"
},
"account_identifiers": { "number": "31131242233323021" }
}'
```
### Update an external account
Update details of a **manually created** account with [Update External Account](/v2/reference/updateexternalaccount). Do not use this for accounts whose details came directly from a vendor.
```shell theme={"system"}
curl \
-X PATCH \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/external_accounts/1542ea0f-09ef-410c-9d66-57f36af5b52b \
--data-binary '
{
"nickname": "Super Exciting Account"
}'
```
## Troubleshooting
Common errors when using the External Accounts API:
| Error | What it means and how to handle it |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ITEM_LOGIN_REQUIRED` | The connection was reset (bank security, password, or MFA change). Re-link the account through Plaid Link in [update mode](#update-mode). If it persists, open a Support ticket with Plaid. |
| `PRODUCTS_NOT_SUPPORTED` | The institution does not support returning all accounts in update mode. This is not an integration problem. |
| `INSTITUTION_NOT_RESPONDING` | The institution is unavailable or under maintenance. Retry later; if persistent, open a "Persistent HTTP 500 errors" Support ticket with Plaid. |
| `NO_AUTH_ACCOUNTS` | Plaid could not find account and routing numbers. The customer may need to opt in to sharing account/routing details during the OAuth flow. |
| Available balance is `null` | Some institutions do not return the available balance. |
## Best practices
* **Verify before debiting** — ACH debits require a verified external account; only ACH credits can be originated without owner verification.
* **Check balances first** — call the balance endpoint before ACH debits to reduce the chance of overdrafts (not available for micro-deposit links).
* **Handle failed accounts** — always inspect `failed_accounts` in add/sync responses rather than assuming every account linked.
* **Unlink before deleting** — deleting in Synctera does not unlink at Plaid; follow the [unlink flow](#unlink-an-external-account) first.
* **Use update mode to re-auth** — resolve `ITEM_LOGIN_REQUIRED` by re-running Plaid Link in update mode instead of recreating the account.
## Related guides
Create the customer an external account belongs to.
Link external accounts to a business customer.
Test data for micro-deposit and linking scenarios.
## API reference
* [Create a link token](/v2/reference/createverificationlinktoken)
* [Create an access token](/v2/reference/createaccesstoken)
* [Add external accounts](/v2/reference/addvendorexternalaccounts)
* [Sync external accounts](/v2/reference/syncvendorexternalaccounts)
* [List external accounts](/v2/reference/listexternalaccounts)
* [Get an external account](/v2/reference/getexternalaccount)
* [Add an external account (manual)](/v2/reference/addexternalaccounts)
* [Update an external account](/v2/reference/updateexternalaccount)
* [Delete an external account](/v2/reference/deleteexternalaccount)
# External Cards
Source: https://docs.synctera.com/v2/docs/external-cards
# Overview
Synctera offers **Instant Payments** to/from non-Synctera issued cards or “External Cards” through partnership with TabaPay.
In these payment scenarios, you act as the merchant and can either initiate an *Instant Account Funding* (`PULL`) or an *Instant Push to Card* (`PUSH`).
## Instant Account Funding
* Also referred to as *Account Funding Transaction (AFT)*
* Is a `PULL` from an external card to fund an account on the Synctera platform
* Can be done through **card-on-file** or through **Apple Pay / Google Pay**
* Can be reversed
## Instant Push to Card
* Also referred to as *Original Credit Transaction (OCT)*
* Is a `PUSH` or a payout from an account on the Synctera platform to an external card
* Can be done through **card-on-file**
* Cannot be reversed, i.e. are irrevocable
## Supported card types and use cases
* Both `PULL` and `PUSH` are supported for:
* Consumer and commercial cards
* Me-to-me transfers only - an MSB (Money Services Business) licence is required for other scenarios, such as P2P and B2B
* Domestic Visa and Mastercard Debit cards - Credit card support may be available upon request
## Added security through tokenization
Instant payments through **card-on-file** and through **Apple Pay / Google Pay** utilize tokenization for added security. Through this process, sensitive cardholder data is converted into a token to protect it from being accessed in the event of a data breach or unauthorized exposure. This process is similar to encryption, which obscures data to make it unreadable.
With **card-on-file**, the card is tokenized through an iFrame and then added as a card-on-file for the customer using the card token. The card token can then be used for initiating payments.
With **Apple Pay / Google Pay**, the customer adds the card to their Apple or Google Wallet, and a payment token is generated for each payment.
This guide will explain these tokenization processes and how to initiate a `PUSH`/`PULL` payment.
See [External Cards API](https://docs.synctera.com/v2/reference/listexternalcards) for more details.
**Onboarding**\
To get setup for Instant Payments with External Cards, please reach out to your Synctera implementation representative.
# External Scores
Source: https://docs.synctera.com/v2/docs/external-scores
External scores let you inject custom, externally sourced risk scores into Synctera's managed Customer Risk Rating (CRR) system.
## Overview
Synctera continuously computes a [Customer Risk Rating (CRR)](/v2/reference/listcrr) for every customer and business. The rating is driven by a set of platform conditions, each contributing a weighted score. In most cases these built-in conditions capture the risk signals you need — but banks and FinTechs often maintain their own risk data outside of Synctera that should influence the rating.
**External scores** give you a controlled way to feed that data in. An external score tells the platform: *"for this customer (or business), apply this externally sourced risk score as an input to the CRR calculation."* The score is used directly as a condition weight, so it flows through the same rating machinery as Synctera's native conditions.
Key characteristics:
* **Scoped** — each external score is tied to a `resource_id` (a `customer_id` or `business_id`) and a `resource_type` (`CUSTOMER` or `BUSINESS`).
* **Weighted** — the `score` (an integer from 0–100) is used directly as a condition weight in the CRR calculation.
* **Overridable** — set `high_risk_override` to `true` to force the resource's computed CRR rating to **high** regardless of the numeric score.
* **Auditable** — an optional `description` and free-form `metadata` object let you record why the score was applied and where it came from.
### When to use external scores
Common scenarios include:
* **Risk / rules engine integration** — A FinTech runs its own transaction-monitoring or rules engine and pushes the resulting risk scores into Synctera so they factor into the managed CRR rating.
* **Compliance-driven high-risk indicators** — Operational staff flag a customer during an ongoing compliance investigation. Creating an external score with `high_risk_override` set to `true` forces the customer's rating to high while the review is underway.
* **Third-party risk data** — Scores sourced from an external vendor (fraud, sanctions, credit) are injected so they contribute to the overall rating alongside Synctera's built-in conditions.
## Prerequisites
This guide assumes you have:
* Created a [personal customer](/v2/docs/create-a-personal-customer) or [business customer](/v2/docs/create-a-business)
You should also be familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
## The external score object
An external score contains the following key fields:
| Field | Description |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `id` | Unique identifier (read-only, assigned on creation). |
| `resource_id` | **Required.** The `customer_id` or `business_id` to assign the score to. |
| `resource_type` | **Required.** The type of resource the score applies to: `CUSTOMER` or `BUSINESS`. |
| `score` | **Required.** An externally sourced risk score (integer, 0–100), used directly as a condition weight in the CRR calculation. |
| `high_risk_override` | When `true`, forces the computed CRR rating to high regardless of the numeric score. Defaults to `false`. |
| `description` | Optional narrative explaining the score rationale. |
| `metadata` | Optional free-form structured metadata supplied by the operator or external system. |
| `tenant` | Tenant id containing the resource (relevant for multi-workspace FinTechs). |
| `creation_time` | When the record was created (read-only). |
| `last_updated_time` | When the record was last updated (read-only). |
```json theme={"system"}
{
"id": "7503cd8a-903b-4fee-aa54-da3dc71f4124",
"resource_id": "5f4ff599-7c29-4f69-a3d9-e103e151afbd",
"resource_type": "CUSTOMER",
"score": 75,
"high_risk_override": false,
"description": "Score fed from FinTech rules engine.",
"metadata": {
"source": "rules-engine",
"model_version": "2.3.1"
},
"tenant": "abcdef_ghijkl",
"creation_time": "2024-01-01T00:00:00.000Z",
"last_updated_time": "2024-01-01T00:00:00.000Z"
}
```
See the [API reference](/v2/reference/listexternalscores) for the full request and response schemas.
## Managing external scores
Use [POST /v2/crr/external\_scores](/v2/reference/createexternalscore). At minimum you must provide a `resource_id`, a `resource_type`, and a `score`.
**Example: Score from a rules engine**
A FinTech's rules engine produces a risk score for a customer and pushes it into Synctera so it contributes to the managed CRR rating:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/crr/external_scores \
--data-binary '
{
"resource_id": "5f4ff599-7c29-4f69-a3d9-e103e151afbd",
"resource_type": "CUSTOMER",
"score": 75,
"description": "Score fed from FinTech rules engine.",
"metadata": {
"source": "rules-engine",
"model_version": "2.3.1"
}
}'
```
**Example: Compliance-driven high-risk indicator**
During an ongoing investigation, operational staff force the customer's rating to high regardless of the numeric score:
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/crr/external_scores \
--data-binary '
{
"resource_id": "5f4ff599-7c29-4f69-a3d9-e103e151afbd",
"resource_type": "CUSTOMER",
"score": 90,
"high_risk_override": true,
"description": "Manual escalation - open compliance investigation (case #4821)."
}'
```
The response includes the system-generated `id`, `creation_time`, and `last_updated_time`:
```json theme={"system"}
{
"id": "7503cd8a-903b-4fee-aa54-da3dc71f4124",
"resource_id": "5f4ff599-7c29-4f69-a3d9-e103e151afbd",
"resource_type": "CUSTOMER",
"score": 90,
"high_risk_override": true,
"description": "Manual escalation - open compliance investigation (case #4821).",
"tenant": "abcdef_ghijkl",
"creation_time": "2024-05-31T14:22:08.123Z",
"last_updated_time": "2024-05-31T14:22:08.123Z"
}
```
Use [GET /v2/crr/external\_scores](/v2/reference/listexternalscores) to list scores. You can filter by `resource_id` and `resource_type`.
```shell theme={"system"}
curl \
-X GET \
-H "Authorization: Bearer $apikey" \
"https://api.synctera.com/v2/crr/external_scores?resource_id=5f4ff599-7c29-4f69-a3d9-e103e151afbd&resource_type=CUSTOMER"
```
To retrieve a single external score by ID, use [GET /v2/crr/external\_scores/\{external\_score\_id}](/v2/reference/getexternalscore):
```shell theme={"system"}
curl \
-X GET \
-H "Authorization: Bearer $apikey" \
"https://api.synctera.com/v2/crr/external_scores/7503cd8a-903b-4fee-aa54-da3dc71f4124"
```
Use [PATCH /v2/crr/external\_scores/\{external\_score\_id}](/v2/reference/updateexternalscore) to modify an existing score. You can update the `score`, `high_risk_override`, `description`, and `metadata` fields.
For example, to raise a customer's score after a fresh evaluation from the rules engine:
```shell theme={"system"}
curl \
-X PATCH \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/crr/external_scores/7503cd8a-903b-4fee-aa54-da3dc71f4124 \
--data-binary '
{
"score": 82,
"description": "Re-scored after new transaction activity.",
"metadata": {
"source": "rules-engine",
"model_version": "2.4.0"
}
}'
```
When an external score is no longer relevant — for example, once a compliance investigation is closed — remove it with [DELETE /v2/crr/external\_scores/\{external\_score\_id}](/v2/reference/deleteexternalscore). The score stops contributing to the CRR calculation.
```shell theme={"system"}
curl \
-X DELETE \
-H "Authorization: Bearer $apikey" \
"https://api.synctera.com/v2/crr/external_scores/7503cd8a-903b-4fee-aa54-da3dc71f4124"
```
## Best practices
External scores directly influence a customer's managed risk rating, and `high_risk_override` bypasses the numeric calculation entirely. Use them deliberately and always with a clear, documented rationale.
* **Record the source** — Use `description` and `metadata` to capture where the score came from (rules engine, vendor, manual escalation) so a compliance reviewer can trace it later.
* **Keep scores fresh** — When your external system re-evaluates a resource, `PATCH` the existing score rather than accumulating stale records.
* **Reserve `high_risk_override` for genuine escalations** — Because it forces a high rating regardless of the numeric score, limit it to situations like active investigations, and remove it once the underlying condition clears.
* **Clean up when no longer relevant** — Delete scores tied to time-bounded events (e.g., a closed case) so they don't keep affecting the rating after the fact.
* **Review regularly** — Use the list endpoint with filters to audit the external scores currently applied to a customer or business.
## API reference
See the full External Scores API reference for request/response schemas and all available parameters:
* [List external scores](/v2/reference/listexternalscores)
* [Create an external score](/v2/reference/createexternalscore)
* [Get an external score](/v2/reference/getexternalscore)
* [Update an external score](/v2/reference/updateexternalscore)
* [Delete an external score](/v2/reference/deleteexternalscore)
# External Vendor KYC/KYB Verification
Source: https://docs.synctera.com/v2/docs/external-vendor-kyc-kyb-verification
Use your own external KYC/KYB vendor with the Synctera platform by submitting the vendor's verification results through the Create Verification API.
## Overview
Banks and financial institutions must perform KYC/KYB on customers before they can move money. These US-regulated programs require data collection, disclosures, identity verification, and ongoing monitoring. While Synctera offers an integrated KYC/KYB option, fintechs may instead use an **external vendor** — provided the sponsor bank approves it before go-live.
**When using an external vendor**, you run the checks with your vendor and then submit the vendor's response to Synctera through the [Create Verification API](/v2/reference/createverification) for each customer or business. This lets Synctera monitor overall risk and approve or reject the customer.
Key characteristics:
* **Vendor-run, Synctera-recorded** — your vendor performs the checks; you record the results in Synctera.
* **Result-driven** — each submission carries an overall `result`; a customer must reach `ACCEPTED` to move money.
* **Typed** — submit one request per verification type (identity, watchlist, document verification, etc.).
* **Detailed** — the `details` object conveys attribute-level outcomes for Synctera's risk team to review.
### Updates as of October 31, 2024
Synctera requires certain fields on the persons and businesses APIs (see step 2 of the process). Review [Required fields for external KYC/KYB vendors](#required-fields-for-external-kyc-kyb-vendors) below — missing fields may cause the customer to fail KYC/KYB.
## Prerequisites
This guide assumes you have:
* Created a [personal customer](/v2/docs/create-a-personal-customer) or [business customer](/v2/docs/create-a-business)
* Recorded a [KYC data collection disclosure](/v2/docs/record-disclosure-acceptance)
* Identified a KYC/KYB vendor and had it [approved](/docs/use-of-external-kyc-vendors) by your sponsor bank
* Integrated the vendor into your onboarding workflow
You should also be familiar with:
* [Need to Know — Environments](/v2/reference/need-to-know#environments)
* [Need to Know — Authentication](/v2/reference/need-to-know#authentication)
The [personal customer](/v2/docs/create-a-personal-customer) and [business customer](/v2/docs/create-a-business) guides cover creating a customer and collecting disclosures; complete these before calling the Create Verification API.
## The external vendor process
The customer enrolls with your fintech, and you call the [person](/v2/reference/createperson) or [business](/v2/reference/createbusiness) API to enroll them with Synctera.
The customer acknowledges KYC/KYB and other disclosures, and you record the acknowledgement with the [disclosure](/v2/reference/createdisclosure) API.
Submit the customer's details to your KYC/KYB vendor. The vendor responds indicating whether the customer passed or failed, with attribute-level detail.
Submit the vendor's response with the [Create Verification API](/v2/reference/createverification), including the verification type, overall result, and details.
Synctera or your team reviews the vendor's response and approves the customer, who can then create an [account](/v2/docs/create-accounts-guide).
## The verification request
Fintechs may request multiple categories of KYC/KYB checks from their vendor (watchlist, document verification, identity, and more). Submit each as its own Create Verification request with the relevant `result`, `verification_type`, and `details`.
### Result
The overall outcome returned by the vendor. A customer must reach `ACCEPTED` to move money and perform most transactions.
| Result | Meaning |
| -------------- | ----------------------------------------------------------------------------------------------- |
| `UNVERIFIED` | No verification exists. |
| `PENDING` | Verification is in progress. |
| `PROVISIONAL` | Partially verified or verified with restrictions. |
| `ACCEPTED` | The customer is verified. |
| `REVIEW` | Verification ran and identified issues requiring review. |
| `VENDOR_ERROR` | Verification did not run due to an unexpected error or failure. |
| `REJECTED` | The customer was rejected and should be blocked from certain actions (e.g. opening an account). |
### Verification type
Indicates the type of check performed. Submit one request per type.
| Type | Description |
| ----------------------- | ----------------------------------------------------------------------------------------------------------- |
| `IDENTITY` | Confirms the provided information matches a real person or business (name, DOB, SSN, etc.). |
| `WATCHLIST` | Checks the customer against watchlists for fraud, money laundering, and suspicious activity. |
| `DOCUMENT_VERIFICATION` | Verifies the authenticity of a document such as a driver's license or passport. |
| `RELATED_ENTITIES` | Dependent checks for related parties (e.g. all beneficial owners must pass KYC for a business to pass KYB). |
| `MANUAL_REVIEW` | The outcome of a manual review; overrides the outcome of other verification types. |
### Details object
Conveys the vendor's attribute-level responses for Synctera's risk team. Provide as much detail as possible.
| Field | Description |
| ------------- | ---------------------------------------------------------------------------------------- |
| `category` | The type of data in the object (e.g. `CIP`, `ADDRESS`, `WATCHLIST`, `DOC_VERIFICATION`). |
| `description` | Human-readable outcome (e.g. "Individual not found on US OFAC watchlist"). |
| `result` | `PASS`, `WARN`, or `FAIL`. |
| `score` | Numeric score from the vendor for the category. |
| `url` | URL from the vendor pertaining to the category. |
| `vendor_code` | Any code provided by the vendor. |
## Examples
### Document verification
Describe which document was examined, whether there was forgery, and whether extraction succeeded — adjust to your vendor's response.
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/verifications \
--data-binary '
{
"person_id": "7ef75751-e372-4c12-9b02-b9e4b1faaac9",
"result": "ACCEPTED",
"verification_type": "DOCUMENT_VERIFICATION",
"verification_time": "2024-10-07T12:00:00Z",
"details": [
{ "category": "DOC_VERIFICATION", "description": "No evidence of tampering or forgery", "result": "PASS" },
{ "category": "DOC_DETAILS", "description": "Received passport as supporting documentation", "result": "PASS" },
{ "category": "DOC_DETAILS", "description": "Successful OCR extraction of document number, expiration date, and nationality", "result": "PASS" }
],
"vendor_info": {
"vendor": "Synctera",
"content_type": "application/json",
"json": { "key1": "value1", "key2": "value2" }
}
}'
```
### Watchlist
Highlight results for primary watchlists (OFAC, PEP); group any remaining watchlists into a single details object.
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/verifications \
--data-binary '
{
"person_id": "7ef75751-e372-4c12-9b02-b9e4b1faaac9",
"result": "ACCEPTED",
"verification_type": "WATCHLIST",
"verification_time": "2024-10-07T12:00:00Z",
"details": [
{ "category": "WATCHLIST", "description": "Individual not found on any watchlists", "result": "PASS", "vendor_code": "I708", "url": "www.synctera.com" },
{ "category": "WATCHLIST", "description": "Individual not found on US OFAC watchlist", "result": "PASS", "vendor_code": "I708" },
{ "category": "WATCHLIST", "description": "Individual not found on any PEP watchlists", "result": "PASS", "vendor_code": "I708" }
],
"vendor_info": {
"vendor": "Synctera",
"content_type": "application/json",
"json": { "key1": "value1", "key2": "value2" }
}
}'
```
### Identity
Describe which attributes (name, address, SSN, email) were checked and the outcome.
```shell theme={"system"}
curl \
-X POST \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
https://api.synctera.com/v2/verifications \
--data-binary '
{
"person_id": "7ef75751-e372-4c12-9b02-b9e4b1faaac9",
"result": "ACCEPTED",
"verification_type": "IDENTITY",
"verification_time": "2024-10-07T12:00:00Z",
"details": [
{ "category": "CIP", "description": "Individual's provided documentation was verified successfully.", "result": "PASS", "vendor_code": "I708" },
{ "category": "ADDRESS", "description": "Address can be resolved to the individual", "result": "PASS", "vendor_code": "I708" },
{ "category": "EMAIL", "description": "Email address is more than 1 year and less than 2 years old", "result": "PASS", "vendor_code": "I708" }
],
"vendor_info": {
"vendor": "Synctera",
"content_type": "application/json",
"json": { "key1": "value1", "key2": "value2" }
}
}'
```
### More `details` object examples
The following snippets show the `details` object across common outcomes:
```json theme={"system"}
// Individual found on one or more watchlists
{ "category": "WATCHLIST", "description": "Individual found on 1 or more watchlists", "result": "WARN", "url": "www.synctera.com", "vendor_code": "I708" }
// Individual found on US OFAC watchlist
{ "category": "WATCHLIST", "description": "Individual found on US OFAC watchlist", "result": "WARN", "vendor_code": "I708" }
// Individual found on PEP watchlist
{ "category": "WATCHLIST", "description": "Individual found on PEP watchlists", "result": "WARN", "vendor_code": "I708" }
// Evidence of tampering or forgery
{ "category": "DOC_VERIFICATION", "description": "Evidence of tampering or forgery", "result": "WARN", "vendor_code": "I708" }
// Driver's license received
{ "category": "DOC_DETAILS", "description": "Received license as supporting documentation", "result": "PASS" }
// Failed OCR extraction
{ "category": "DOC_DETAILS", "description": "Failed OCR extraction of name, dob, sex, and country", "result": "WARN", "url": "www.synctera.com" }
// Selfie capture success
{ "category": "SELFIE_CAPTURE", "description": "Successfully matched selfie to document portrait", "result": "PASS", "score": 100 }
// Selfie capture failed
{ "category": "SELFIE_CAPTURE", "description": "Selfie failed to match to document portrait", "result": "WARN" }
// Customer info matched OCR data
{ "category": "DOC_DETAILS", "description": "Successfully matched customer information to OCR extracted data fields", "result": "PASS" }
// Customer info failed to match OCR data
{ "category": "DOC_DETAILS", "description": "Failed to match customer information to OCR extracted data fields name, dob, sex and country", "result": "WARN" }
// Customer info matched third-party services
{ "category": "CIP", "description": "Individual's provided documentation was verified successfully.", "result": "PASS" }
// Address could not be matched
{ "category": "ADDRESS", "description": "Address cannot be resolved to individual", "result": "WARN" }
```
## Required fields for external KYC/KYB vendors
As of October 31, 2024, more stringent sponsor bank requirements make the following fields required. Provide them, or the customer may fail KYC/KYB.
**Personal customers** (see the [persons API](/v2/reference/createperson) and [persons guide](/v2/docs/create-a-personal-customer)):
| Field | Notes |
| ------------------ | -------------------------------------------------- |
| `dob` | |
| `email` / `phone` | At least one is required. |
| `first_name` | |
| `last_name` | |
| `legal_address` | address line 1, city, state, postal code, country. |
| `shipping_address` | address line 1, city, state, postal code, country. |
| `personal_ids` | type, id, and country code. |
**Business customers** (see the [businesses API](/v2/reference/createbusiness)):
| Field | Notes |
| ----------------- | ---------------------------------------------------------------------------- |
| `ein` | Not required for sole proprietors. |
| `email` / `phone` | At least one is required. |
| `entity_name` | |
| `legal_address` | address line 1, city, state, postal code, country. PO Box is not acceptable. |
## Best practices
A customer must reach an overall `result` of `ACCEPTED` before they can move money or open accounts. Submit the vendor response promptly and provide rich `details` so Synctera's risk team can review thoroughly.
* **Get sponsor-bank approval first** — external vendors must be approved before go-live.
* **Submit one request per type** — send separate Create Verification calls for identity, watchlist, and document verification.
* **Maximize detail** — include as many `details` objects as the vendor returns; richer data speeds review.
* **Provide the required fields** — supply all October 2024 required person/business fields to avoid KYC/KYB failures.
## Related guides
Enroll a person before submitting vendor results.
Enroll a business before submitting vendor results.
Compare with Synctera's integrated verification.
Capture the KYC data collection disclosure.
## API reference
* [Create a verification](/v2/reference/createverification)
* [Create a person](/v2/reference/createperson)
* [Create a business](/v2/reference/createbusiness)
* [Create a disclosure](/v2/reference/createdisclosure)
# Fees and Rewards
Source: https://docs.synctera.com/v2/docs/fees-and-rewards
Fees and Rewards are important features for many FinTech programs. Fees allow charging customers for various services and transactions, thereby helping to provide a steady revenue, and cover costs for the related services and transactions. Reward programs enable crediting customers for specific financial behaviors, which can help FinTechs drive user acquisition, engagement and habit building, increase customer retention and create cross-selling opportunities.
## Overview
Synctera supports 2 paths for fee/reward orchestration:
**1. Orchestration by FinTech (you):**
* You use the [Synctera Fee API](/v2/reference/createfeetemplate) for the setup and orchestration.
* You first create a **Fee/Reward Template**, which is used to specify the type of fee or reward, and to set default values, e.g. an amount or description. Designed for flexibility, you can choose which internal account is used by each template, allowing granular management of your fees and reward programs.
* You then create the Fee/Reward, i.e. apply to customer accounts, using the Fee/Reward Template.
* This setup is described in the guide below.
**2. Orchestration by the Synctera system:**
* You use the Synctera Console to set up **Fee/Reward Products** and link to account templates. The Synctera system then automatically applies the fees and rewards to customer accounts based on the setup.
* This setup is described in [this article](https://docs.synctera.com/overview) in our Learning Center.
* NOTE: You can also use the [Synctera Fee Product API](/v2/reference/createfeeconfig) for the setup, although the Console is recommended as this is typically a one-time or a low-frequency task.
In this guide, you will walk through the steps to set up a simple membership fee and reward program following the first path (orchestration by FinTech). The scenario will be offering two tiers of membership to customers. The first tier charges a \$5 monthly fee. The second tier charges a \$10 monthly fee but gives the customer cash back on certain types of purchases.
## Prerequisites
* You have created customers, either [personal](/v2/docs/create-a-personal-customer) or [business](/v2/docs/create-a-business)
* You have created [accounts](/v2/docs/create-accounts-guide) for these customers.
* [Internal accounts](/v2/reference/listinternalaccounts) have been created based on how you want to group your fee and reward transactions. Please ask Synctera Operations to create these.
### Important Note!
All fees charged to customers must be clearly disclosed in the initial customer account agreement in a manner consistent with federal law. Prior to changing any fee structure, please consult with Synctera's compliance team as well as your Sponsor Bank.
## Creating Fee and Reward Templates
### Create a Fee Template
First, create a fee template for the \$5 monthly fee. To do this, use the [Create Fee Template](/v2/reference/createfeetemplate) endpoint.
```shell Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/fee_templates/ \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
--data-binary '{
"amount":500,
"currency":"USD",
"description":"Tier 1 Premium Monthly Fee",
"internal_account_id": "",
"is_enabled":true,
"subtype":"MAINTENANCE"
}'
```
The response will look like this:
```json JSON theme={"system"}
{
"id": "5f9b3b5a-5b0a-4b0a-8b0a-5b0a5b0a5b0a",
"creation_time": "2021-10-01T00:00:00.000000Z",
"last_updated_time": "2021-10-01T00:00:00.000000Z",
"amount": 500,
"currency": "USD",
"description": "Tier 1 Premium Fee",
"internal_account_id": "",
"is_enabled": true,
"subtype": "MAINTENANCE"
}
```
Note the `id` field, you will need this later when creating the fees.
Also note that the `subtype` is used as the transaction description on Account Statement / Transaction List.
A Tier 2 template can be created using a similar request, but setting the amount to \$10, and a different description:
```shell Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/fee_templates/ \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
--data-binary '{
"amount":1000,
"currency":"USD",
"description":"Tier 2 Monthly Fee",
"internal_account_id": "",
"is_enabled":true,
"subtype":"MAINTENANCE"
}'
```
### Create a Reward Template
Next, create a reward template for cash back on purchases. To do this, use the [Create Reward Template](/v2/reference/createrewardtemplate) endpoint.
```shell Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/reward_templates/ \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
--data-binary '{
"currency":"USD",
"description":"Tier 2 Cash Back Reward",
"internal_account_id": "",
"is_enabled":true,
"subtype":"CASHBACK"
}'
```
Notice how the reward template does not contain a default amount. Your system will need to calculate the reward amount based on the amount of customer purchases follow the example below for more details.
1. Setting Up Webhooks
* You will need to register a webhook to listen for specific transaction events. These events can notify your system when a transaction occurs, such as `TRANSACTION.POSTED.CREATED` (such as a successful card purchase) or `EXTERNAL_CARD_TRANSFER.CREATED` (money transferred from an external source).
* For details on how to register webhooks, refer to the [webhooks](/v2/docs/webhooks-guide) documentation.
2. Understanding the Webhook Payload
* When a `TRANSACTION.POSTED.CREATED` event occurs, your system will receive a notification with details about the transaction. Here's an example of a payload for a card purchase webhook event:
```json JSON theme={"system"}
{
"id": 13879,
"uuid": "8e04f62d-4eea-4217-bf2c-b77f569f5515",
"type": "card",
"subtype": "pos_purchase", // Point-of-sale purchase
"status": "POSTED",
"reference_id": "63352",
"data": {
"user_data": {
"account_id": "189b0d33-3359-485c-8a77-24fa726ca817", // Unique ID for the user's account
"amount": 1868, // Transaction amount in cents (e.g., 1868 cents = $18.68)
"card_id": "a3041aee-4b97-4dd8-8a8d-c55571fe9953",
"card_transaction_subtype": "pindebit",
"currency_code": "USD"
}
},
"posted_date": "2024-03-08T00:00:00Z", // Date the transaction was settled
"effective_date": "2024-03-07T00:00:00Z", // Date the transaction took place
"created": "2024-03-07T20:40:51.259048Z", // Date and time the webhook notification was created
"updated": "2024-03-07T20:40:51.040607Z", // Date and time the webhook notification was last updated
"transaction_time": "2024-03-07T20:40:10.374925Z" // Date and time the transaction occurred
}
```
3. Calculating Rewards/Fees
* Based on your program's rules, calculate the amount of reward or fee to be applied. For example, if your program offers a 5% reward on transactions, you would calculate the reward amount as follows: `Reward amount = Transaction amount (in cents) x Reward percentage` In the example above, the transaction amount is 1868 cents and the reward percentage is 5%. Therefore, the reward amount would be: `Reward amount = 1868 cents x 0.05 = 93.4 cents (rounded down to 93 cents)`
4. Applying Rewards/Fees
* Once you have the account ID (from the payload) and the calculated reward/fee amount, follow the instructions listed below to apply the reward to a designated reward template.
## Apply Fees and Rewards to Accounts
Now that you have created fee and reward templates, you can apply them to customer accounts. To do this, use the [Create Fee](/v2/reference/createfee) and [Create Reward](/v2/reference/createreward) endpoints.
To create the fees, use requests like this:
```shell Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/fees/ \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
--data-binary '{
"account_id": "{CUSTOMER_ACCOUNT_ID}",
"template_id": "{FEE_TEMPLATE_ID}"
}'
```
No amount, currency, or description is needed because the values are set in the template.
Applying the reward is very similar but the calculated amount is be required:
```shell Shell theme={"system"}
curl \
-X POST \
$baseurl/v1/rewards/ \
-H "Authorization: Bearer $apikey" \
-H 'Content-Type: application/json' \
--data-binary '{
"account_id": "{CUSTOMER_ACCOUNT_ID}",
"template_id": "{REWARD_TEMPLATE_ID}",
"amount": "{CALCULATED_AMOUNT}"
}'
```
## Viewing Applied Fees and Rewards
To query what fees and rewards have been applied, use the [List Fees](/v2/reference/listfees) and [List Rewards](/v2/reference/listrewards) endpoints.
Fees and rewards also can also be seen in the transactions list: use the [List Posted Transactions](/v2/reference/listpostedtransactions) endpoint with a transaction type of `fee` or `reward`.
# Get Started
Source: https://docs.synctera.com/v2/docs/get-started
Most Synctera APIs act on resources whose IDs are specified in requests. These resources must exist. This means there is a sequence to calling the APIs to get from a clean slate to a successful transaction.
This document is aimed at capturing that sequence, which is a reflection of a typical customer journey. We have provided the exact API details here but we strongly encourage you to read the Guides mentioned below to understand the dependencies, alternatives, configuration needs and additional information about these products.
You can also explore and try out the [Postman collection](https://www.postman.com/synctera/workspace/synctera-public-workspace/collection/19487588-931708f8-bc42-4051-8a02-4e9104d984e4?action=share\&creator=19487588) once you have an API key.
## Before You Begin
### Get an API Key
If you don't already have an API key, you will need one in order to call the APIs. If you have signed up for the t-minus10 program ([click here](https://drive.google.com/file/d/1qPYNx4Ij1el5lIKbfJcJttQJb4Ot9xny/view?usp=sharing) for more details on t-minus10) you can self-serve an API key by following the "Sandbox" or "Live testing" links in the top navigation. If you are already working with the Synctera team, you an also reach out to your contact at Synctera to get an API key.
**Self-serve**:
[https://app.synctera.com/admin/developer-tools/api-keys](https://app.synctera.com/admin/developer-tools/api-keys)
[https://app-sandbox.synctera.com/admin/developer-tools/api-keys](https://app-sandbox.synctera.com/admin/developer-tools/api-keys)
Once an API key is issued in Sandbox, you can immediately start calling the APIs below. In Live Testing or Production, your FinTech must first pass KYB.
### KYB Your FinTech
In Sandbox - you're done! KYB was automatically passed for your Sandbox workspace.
In Live Testing or Production, you need to have run a successful KYB on your Fintech if you want to issue cards or move money via ACH / Internal transfers.
If you are on t-minus 10, you need to do this by filling in [Business information](https://app.synctera.com/admin/my-fintech/address) and [Owner details](https://app.synctera.com/admin/my-fintech/owners) and then [Run KYB](https://app.synctera.com/admin/my-fintech/kyb) using the t-minus 10 platform.
If you are not on t-minus10, your contact at Synctera will collect the required information to run KYB for you.
## Guides and APIs
These are customers of the Financial Service that use Synctera APIs. Customers can either be Individuals or Businesses. If your customers are businesses, create a business.
**Guide**:
**APIs**:
Customers or Businesses who are onboarded to your platform will need to accept disclosures for legal needs. You will work with Synctera Implementation & Onboarding team to identify the disclosures needed for your company.
**Guide**:
**API**:
The next step is to ensure that you know your customer and they are who they claim to be. Run KYC if your customer is an individual. Run KYB if your customer is a business.
**Guide**:
**API**:
Account templates define basic characteristics of an account. This is a mandatory step to create an account.
You can also use Account Products which can be included in the account template to capture the Interest / Fee aspects of the account.
**Guide**:
**API**:
Once a Customer is created, Verified, and an account template is created, you can now create an account for the customer / business.
You can use Account relationships to create further relationships on the accounts - e.g Joint Account Holder.
**Guide**:
**APIs**:
Once an account is created, the next step is to move funds from an external bank account to move funds into the customer’s account at the Fintech.
Once an account is created, the next step is to move funds from an external bank account to move funds into the customer’s account at the Fintech.
**Guide**:
**APIs**:
To move funds into a customer account, you would typically link a customer's external account and move funds from there. Alternatively, as a convenience in the sandbox environment, you can also create an internal account to do this. Internal accounts in production would typically be used for specific needs such as issuing promotional credits to your customers. To create an internal account, use this API.
**API**:
To move funds into a customer account, you would typically link a customer's external account and move funds from there. Alternatively, as a convenience in the sandbox environment, you can also create an internal account to do this. Internal accounts in production would typically be used for specific needs such as issuing promotional credits to your customers. To create an internal account, use this API.
**API**:
Once the account is funded, the customer access the money on the account using cards or ACH. Users can be issued multiple virtual cards and a physical card per account.
You will have to use Marqeta widgets for accessing card details. You also can issue custom Cards with images uploaded by the user.
Once an external account is linked (or you created an internal account as above), you can move funds from an external account into the customer’s account at the Fintech.
**API**:
Once the account is funded, the customer access the money on the account using cards or ACH. Users can be issued multiple virtual cards and a physical card per account.
Once the account is funded, the customer access the money on the account using cards or ACH. Users can be issued multiple virtual cards and a physical card per account.
You will have to use Marqeta widgets for accessing card details. You also can issue custom Cards with images uploaded by the user.
**Guides**:
**API**:
You can also move money between accounts held in the Fintech.
You can also move money between accounts held in the Fintech.
**API**:
# In-Store Cash Deposits
Source: https://docs.synctera.com/v2/docs/in-store-cash-deposits
Enable customers to deposit physical cash at retail locations using barcodes through InComm's VanillaDirect solution.
# Overview
Synctera offers in-store cash deposits through InComm's VanillaDirect solution. With this solution, customers generate a barcode on demand, and present it to a cashier at an InComm-supported retailer (e.g., 7-Eleven, Walgreens, CVS), which allows them to deposit physical cash directly into their account. This method provides an accessible alternative when ATMs or traditional banking options are not available.
## Barcode UX build
As you own the user experience, you are responsible for building the barcode UX in your app, which includes:
* Generating a barcode
* Displaying the barcode and payment slip with the required information
* Presenting the transaction details/receipt
* Providing access to the retailer map
The UX flow follows specific requirements, which are described below along with implementation details.
## Prerequisites
Prerequisites for creating barcodes:
* A [Customer](/v2/docs/create-a-personal-customer) has been created
* An [Account](/v2/docs/create-accounts-guide) for the customer has been created
# Managing Barcodes
## Create Barcode
Barcodes should be generated through the customer's app shortly before they intend to make a deposit using the [POST /v2/cash/barcodes](/v2/reference/createbarcode) endpoint. Each barcode is single-use, is valid for **15 minutes** and must be used at a participating retail location within a **15-mile radius** of the location where it was generated.
### Example Request
```sh Shell theme={"system"}
curl \
-X POST \
$baseurl/v2/cash/barcodes \
-H "Authorization: Bearer $apiKey" \
--json '{
"type": "CASH_DEPOSIT",
"account_id": "019833f2-794b-7b6f-9bf1-ddbf935a74dd",
"customer_id": "019833f2-972c-7b0e-ab52-1414133ed001",
"customer_latitude": 37.7749,
"customer_longitude": -122.4194,
"tenant": "gxpwkp_eyzdoy",
"metadata": {
"test": "test_value",
"test2": "test_value2"
}
}'
```
### Example Response
```json JSON theme={"system"}
{
"account_id": "019833f2-794b-7b6f-9bf1-ddbf935a74dd",
"barcode_number": "799366476346399810001917770198",
"creation_time": "2025-06-17T23:12:28.572154Z",
"currency": "USD",
"customer_id": "019833f2-972c-7b0e-ab52-1414133ed001",
"customer_latitude": 37.7749,
"customer_longitude": -122.4194,
"id": "019833f4-a7b3-7107-be10-a89c1dcdf510",
"last_updated_time": "2025-06-17T23:12:28.572154Z",
"max_amount": 900,
"metadata": {
"test": "test_value",
"test2": "test_value2"
},
"min_amount": 10,
"status": "AVAILABLE",
"tenant": "gxpwkp_eyzdoy",
"timestamp_valid_to": "2025-06-17T23:22:28.044478Z",
"type": "CASH_DEPOSIT"
}
```
## Block Barcode
If a barcode has been generated but needs to be **blocked** to prevent further use, you can update its status to `BLOCKED` using the [PATCH /v2/cash/barcodes/\{barcode\_id}](/v2/reference/updatebarcode) endpoint.
Once a barcode is blocked, it cannot be reactivated. A new barcode must be generated to enable subsequent deposits.
### Example Request
```sh Shell theme={"system"}
curl \
-X PATCH \
$baseurl/v2/cash/barcodes/019833f4-a7b3-7107-be10-a89c1dcdf510 \
-H "Authorization: Bearer $apiKey" \
--json '{
"status": "BLOCKED"
}'
```
### Example Response
```json JSON theme={"system"}
{
"account_id": "019833f2-794b-7b6f-9bf1-ddbf935a74dd",
"barcode_number": "799366476346399810001917770198",
"creation_time": "2025-06-17T23:12:28.572154Z",
"currency": "USD",
"customer_id": "019833f2-972c-7b0e-ab52-1414133ed001",
"customer_latitude": 37.7749,
"customer_longitude": -122.4194,
"id": "019833f4-a7b3-7107-be10-a89c1dcdf510",
"last_updated_time": "2025-06-17T23:12:28.572154Z",
"max_amount": 900,
"metadata": {
"test": "test_value",
"test2": "test_value2"
},
"min_amount": 10,
"status": "BLOCKED",
"tenant": "gxpwkp_eyzdoy",
"timestamp_valid_to": "2025-06-17T23:22:28.044478Z",
"type": "CASH_DEPOSIT"
}
```
## Displaying Barcodes
Barcodes should be generated using the **Code 128C** format to ensure compatibility with retailer point-of-sale (POS) systems. Adhering to the specifications below will help ensure reliable scanning and processing.
### Printable Barcode Requirements
* **Barcode format**: Must use **Code 128C**
* **Color**: Black
* **Minimum width**: 2.25 inches
* **Minimum height**: 5/8 inch
* **Quiet zone**: At least 1/8 inch of white space above and below the barcode
* **Module size**: Minimum of 13 mils (for 300 DPI resolution); the module is the smallest unit width of a bar
* **Human-readable text**: The barcode value must be printed below the barcode
* **File format**: It is strongly recommended that barcodes be generated in **vector format** (e.g., SVG or PDF) to maintain scan quality across different devices and resolutions
### Displaying on Smartphone
Customers may present digital barcodes on their smartphones at the point of sale. For optimal scan performance, users should rotate their device to **landscape orientation**, increasing the barcode’s width and improving scanner readability.
The requirements for digital barcodes are the same as for printed versions. Specifically:
* The barcode must follow the **Code 128C** format and all associated size and spacing requirements
* A **human-readable number** must be displayed below the barcode to allow manual entry by the cashier if scanning fails
## Payment Stack Screen
The option to Add/Deposit Cash must be made available to the customer in the app. The recommended best practice is to add the Cash Payment option at the top of the payment stack/options.
## Payment Slips
Payment slips should be generated and provided to your customers, containing both the **barcode image** and the **human-readable barcode number**.
While the layout of the slip can follow your brand’s design and formatting standards, it must include the following required elements:
1. **Payment Range**
Display the minimum and maximum deposit amounts allowed for the barcode.
2. **Barcode Expiration Notice**
Clearly state the time at which the barcode expires.
3. **VanillaDirect Ice Cream Cone**
Include the official InComm VanillaDirect branding icon.
4. **Barcode**
Display a scannable **Code 128C** barcode, along with the **human-readable number** printed below.
5. **Cash Deposit Fee**
If applicable, display any fees configured for the cash deposit.
6. **Terms & Conditions**
Include a reference to the **VDBS Terms of Service**.
7. **Consumer Step-by-Step**
Provide clear, numbered instructions that explain how to complete the cash deposit at a participating retailer.
8. **Store Locator**
Include a link or embedded map to help customers find eligible retail locations near them where the barcode can be used. The recommendation is to enable both map view and list view.
As this feature requires the phone's **location services** to be turned on, screens for notifying the customer about location services being disabled and for prompting the user to allow location services are also required.
9. **Receipt**
Provide the ability for your customer to display transaction details after completing a deposit.
The transaction's [user\_data](/v2/docs/transactions-guide#user_data) field contains barcode-specific payment details, including barcode information and merchant location data, which you can use to build a comprehensive receipt:
```json JSON theme={"system"}
{
"barcode": {
"barcode_number": "839650172948537162049873516238",
"biller_id": "X8v2QmPzYdR5aLtKBwN1oCgHUEsJfT63",
"pos_timestamp": "2014-03-17T11:13:36Z",
"customer_longitude": -73.935242,
"customer_latitude": 40.73061,
"external_device_id": "738920164537",
"metadata": {
"product_id": "123456",
"order_id": "NB-3406542",
"comment": "any text"
}
},
"merchant": {
"name": "7 Eleven",
"retailer_reference_number": "98765",
"address1": "address1",
"address2": "",
"city": "City",
"county": "County",
"state": "GA",
"zip_code": "11223-3445",
"country": "USA",
"phone_numbers": [
"123456789"
],
"terminal_id": "25476001",
"lat": 33.7380073,
"lng": -84.3936426,
"store_id": "123345",
"store_external_id": "25476"
}
}
```
Additionally, customers can retrieve their E-receipt directly from InComm by providing the barcode number at [pay.vanilladirect.com/ereceipt](https://pay.vanilladirect.com/ereceipt).
Please reach out to your Synctera Implementation Representative for detailed UX/UI specifications, and for sample UX flows.
10. **Optional screens**
* Barcode expiration screen: To enable customer to generate a new barcode
* Monthly/daily deposit limit screens: To notify customer about limit being reched and when it will be reset
# In-Store Cash Deposit Fees
Cash deposit fees can be automatically deducted from the customer’s account at the time the deposit is processed. To enable this, you must configure a **cash deposit fee** with a specified amount in your system.
If a transaction is reversed, the associated fee will also be automatically reversed.
Our system supports only **one active cash deposit fee** at a time. It is strongly recommended that you configure **a single fee** to apply to all barcode-based cash deposit transactions.
If multiple fees are configured, the **most recently created** fee will be prioritized and applied.
For setup instructions, refer to the [**Fees**](/v2/docs/fees-and-rewards) section in the developer guide.
# Point of Sale Interaction
When a customer presents a barcode to the cashier, a **test transaction** is initiated that results in an **authorization** on the customer’s account. If the authorization succeeds, the payment is applied and posted to the customer’s account.
Once the **payment application is successfully processed**, the cashier will accept the cash from the customer.
At this point, the transaction is considered final and cannot be refunded or reversed.
In the event of a **network timeout** or other communication failure during the transaction, the cashier will **not accept the cash**, and a **void payment** will be processed automatically. This ensures that no funds are posted to the customer's account and that the authorization is correctly canceled.
Multiple test payments can be performed and are valid for authorization. However, only the **apply payment** finalizes the deposit, prompting the cashier to accept the cash and post the funds to the customer's account.
## Available Retail Locations
To help your customers find nearby participating retailers where they can make cash deposits, you can either:
* **Embed** a retailer map directly within your app using an `