· 18 min read
Sumsub for Armenian CASPs, end to end
From an empty Sumsub account to verified customers whose records meet Armenian rules: levels, questionnaires, keys, webhooks, retention and testing — with every requirement traced to its article.
This article explains public rules to help teams plan. It is not legal advice. Check the current versions of the acts and review your setup with your AML/CFT officer and counsel.
What you will have at the end
This guide is for crypto-asset service providers and brokers in Armenia whose compliance officer and engineers set up Sumsub together. It follows the rules that exist today: the AML/CFT Law (as amended through July 2025), the Law on Non-Cash Operations, the Central Bank's Decision 12-N of 26 January 2026, and the Law on Protection of Personal Data.
- A standard level for individuals that collects everything Armenian law asks for — not just what a generic KYC template collects.
- An enhanced level for PEPs and high-risk customers, and a company (KYB) level with the Armenian 20% beneficial-ownership threshold.
- Signed API access and verified webhooks, with personal data kept out of logs.
- Your own copies of the records you must keep for five years, even though Sumsub did the checking.
flowchart LR
C["Customer"] -->|"verification link"| S["Sumsub level"]
S -->|"webhook: applicantReviewed"| B["Your backend"]
B -->|"GET applicant"| S
B --> K[("Your KYC records<br/>5 years after the relationship")]
B --> L["Application logs<br/>no personal data"]
B --> D["Decision: approve / more docs / reject"]
K -->|"on request, 5 working days"| CBA["Central Bank of Armenia"]What Armenian rules ask you to collect
Article 16(4)(1) of the AML/CFT Law lists the minimum identification data for a natural person, taken from an identity document that bears a photograph. Several items are routinely missed by default KYC flows — note the place of birth and the document issue date.
| What | Required by | How you get it with Sumsub |
|---|---|---|
| First name, last name | AML Art. 16(4)(1); Non-Cash Operations Art. 6.1(3) | Identity document step |
| Citizenship | AML Art. 16(4)(1) | Passport country / nationality; questionnaire for other citizenships |
| Date and place of birth | AML Art. 16(4)(1) | Document for the date; place of birth from the document or the questionnaire |
| Document series, number and issue date | AML Art. 16(4)(1); Non-Cash Operations Art. 6.1(3) | Identity document step — check each applicant |
| Registration address (if any) and place of residence | AML Art. 16(4)(1) | Questionnaire, or Proof of residence |
| Residency (resident / non-resident) | Decision 12-N point 3 | Questionnaire |
| PEP status (and of beneficial owners) | AML Art. 18(2.1) | Questionnaire plus AML screening |
| Business profile, purpose and intended nature | AML Art. 16(7) | Questionnaire |
| Core of vital interests (foreign persons) | AML Art. 18(4) | Questionnaire, only for foreign customers |
| Source of funds and of wealth, senior management approval | AML Art. 3(1)(22) — enhanced CDD | Enhanced level; approval is recorded in your own system |
| Copy of the identity document | Decision 12-N point 4; AML Art. 16(8) | Download from Sumsub and keep your own copy |
Which level applies is a risk decision (AML Art. 18). Any high-risk criterion — a politically exposed person, a link to a non-compliant country, unusual activity, or an assignment from the Central Bank — means enhanced due diligence. Simplified due diligence needs a low-risk criterion and no suspicion; in the current law it reduces ongoing monitoring, not the identification data.
flowchart TD
A["New customer"] --> B{"Any high-risk criterion?<br/>PEP, non-compliant country,<br/>unusual activity, CBA assignment"}
B -->|"yes"| E["Enhanced CDD<br/>am-individual-enhanced"]
B -->|"no"| C{"Low-risk criterion<br/>and no suspicion?"}
C -->|"yes"| S["Simplified CDD<br/>same identification data,<br/>lighter monitoring"]
C -->|"no"| N["Standard CDD<br/>am-individual-standard"]
E --> R["Review at least yearly"]
N --> R
S --> RAccount, modes and paperwork
- Sumsub has two modes, Sandbox and Production, with separate data and separate app tokens. Build everything in Sandbox first.
- Invite your compliance officer and engineers as team members with only the permissions they need, and turn on two-factor authentication.
- Sign Sumsub's data processing agreement. Under the Personal Data Law (Art. 14) a processor acts on a written assignment that names the purpose, the data and the protection measures — this is that document.
- Sumsub processes data outside Armenia. Check the transfer rules of Art. 27 of the Personal Data Law (adequate-protection countries, or authorisation by the Personal Data Protection Agency) and keep the result in your compliance file.
Create the Armenian CDD questionnaire
Go to Questionnaires → Create questionnaire. Sumsub generates ids for the questionnaire, its sections and questions; edit them to the ids below so your code (and the open-source kyc-am mapping) can read the answers without changes.
| Item id | Type | Question | Ask |
|---|---|---|---|
| biometric_consent | Consent (bool) | I agree that my photo and video are processed to check my identity. | Always, first |
| residence_address | Paragraph | Where do you live? (full address) | Always |
| registration_address | Paragraph | Registration address, if different | Optional |
| place_of_birth | Short answer | Place of birth | When the document does not show it |
| residency_status | Multiple choice | Are you a resident of Armenia? | Always |
| other_citizenships | Country (multiple) | Other citizenships (if any) | Optional |
| pep_status | Multiple choice | Are you a politically exposed person, or a family member or close associate of one? | Always |
| purpose_and_nature | Paragraph | What will you use our services for? | Always |
| business_profile | Paragraph | Occupation or business, and expected monthly volume (AMD) | Always |
| core_of_vital_interests | Paragraph | Where are your family and main economic interests based? | Foreign customers (use a condition) |
Name the questionnaire and its section am_cdd. For the enhanced level, create a second questionnaire am_edd with two paragraph questions, source_of_funds and source_of_wealth. Conditions refer to questions as sectionid.itemid.
Questionnaire answers are the customer's own statements. Treat them as declarations; verify what the law requires you to verify (for example PEP status also through AML screening).
The standard level for individuals
- Go to the Individuals page → Create level. Name it am-individual-standard (level names are case-sensitive in the API).
- Steps tab, in this order: Questionnaire (am_cdd) — so the biometric consent comes before the selfie; Identity document — passport, ID card, residence permit; Liveness & Face match.
- Email and phone verification are optional; useful as contact data.
- Configurations tab: in ID document settings turn on "Accept only valid documents"; turn on AML screening so PEP and sanctions hits are checked, not only declared.
- Save, then use Run level to try it yourself in Sandbox.
sequenceDiagram
actor Customer
participant App as Your app
participant S as Sumsub
App->>S: create applicant (externalUserId = your customer id)
App->>S: create verification link
App-->>Customer: send link
Customer->>S: consent + questionnaire
Customer->>S: identity document
Customer->>S: liveness and face match
S->>S: document, face and AML checks
S-->>App: webhook applicantReviewed
App->>S: GET applicant (data + questionnaire answers)
App->>App: map to Armenian fields, list gaps, decideSumsub extracts the document number and issue date from most documents, but you cannot mark them as mandatory. Armenian rules require both, so check them on every applicant and, if one is missing, get it through the questionnaire or manual review before you approve.
The enhanced level
- Duplicate am-individual-standard as am-individual-enhanced.
- Add Proof of residence (a recent utility bill or bank statement).
- Add the am_edd questionnaire: source of funds and source of wealth — both required for enhanced due diligence (AML Art. 3(1)(22)(b)).
- Add a document step for evidence of the source of funds (salary statement, sale contract, tax return).
- Record senior management approval in your own system: who approved and when. Sumsub does not hold this for you.
POST /resources/applicants/{applicantId}/moveToLevel?name=am-individual-enhanced
// with kyc-am
await sumsub.moveToLevel(applicantId, "am-individual-enhanced");The company level (KYB)
Go to the Companies page → Create level and name it am-company. For legal persons the law asks for the name, registered office, place of business when different, the charter, the state registration number, the executive officer's name and, if available, the tax number (AML Art. 16(4)(2)); Decision 12-N also asks for the tax number and the representative's identification data.
- Company documents: registry extract or registration certificate, charter (կանոնադրություն), shareholder register, and a document on the powers of management bodies (Art. 16(6)).
- Beneficial owners start at 20% in Armenia — direct or indirect holding, or control by other means; if nobody qualifies, the senior managing official (Art. 3(1)(14)). Many tools default to 25%: set 20% in your beneficiary settings, or collect holders from 20% yourself.
- Verify the executive officer, representatives and beneficial owners as individual applicants with am-individual-standard.
- Write down how you determined the beneficial owners. The law requires the reasoning to be documented and kept (Art. 16(5.1)).
flowchart TD
C["Company customer"] --> A{"Anyone holding 20% or more,<br/>directly or indirectly?"}
A -->|"yes"| O["Beneficial owner(s)"]
A -->|"no or not only"| B{"Anyone controlling<br/>by other means?"}
B -->|"yes"| O
B -->|"no one found"| M["Senior managing official<br/>is the beneficial owner"]
O --> V["Identify and verify,<br/>check PEP status"]
M --> V
V --> R["Document how you decided<br/>(Art. 16(5.1))"]Where each Armenian field comes from
| Field | Sumsub source | Watch out for |
|---|---|---|
| first_name, last_name, patronymic | info.firstName / lastName / middleName | — |
| date_of_birth | info.dob | — |
| place_of_birth | info.placeOfBirth, else questionnaire | Often empty for ID cards |
| citizenship | info.nationality or passport country | Ask for other citizenships |
| id_document_number, id_document_series | idDocs[].number, serialNumber | Check per applicant |
| id_document_issue_date | idDocs[].issuedDate | Not every document yields it |
| residence_address | info.addresses, questionnaire, or Proof of residence | — |
| registration_address | questionnaire, or address on the ID card | Collect if it exists |
| residency_status, pep_status, purpose_and_nature, business_profile | questionnaire am_cdd | Declarations |
| source_of_funds, source_of_wealth | questionnaire am_edd | Enhanced level only |
| id_document_copy | document images API | Keep your own copy |
The open-source module kyc-am does this mapping (toDictionary) and returns the list of Armenian-required fields still missing for the level, each with the article behind it.
App token, secret and IP allowlist
- Switch to the mode you are configuring (Sandbox first). Tokens only work in the mode they were created in.
- Go to Dev space → App Tokens → Generate app token.
- Give only the permissions your backend needs: create and read applicants, generate links, read document images, change levels.
- Under Whitelisted IPs, restrict the token to your servers' addresses.
- The token and secret are shown once. Put them straight into your secret manager — never in the database next to KYC data, never in logs.
import { SumsubClient } from "kyc-am/sumsub";
const sumsub = new SumsubClient({
appToken: process.env.SUMSUB_APP_TOKEN, // "sbx:…" in Sandbox, "prd:…" in Production
appSecret: process.env.SUMSUB_APP_SECRET,
});
await sumsub.testConnection(); // { ok: true } — creates nothing
await sumsub.ensureApplicant("am-individual-standard", { externalUserId: "C-000123", email: "anna@example.am" });
const { url } = await sumsub.createLink("am-individual-standard", { externalUserId: "C-000123" });Use your own customer identifier as externalUserId — the same identifier Decision 12-N requires in every record — never a document number or an email address.
Webhooks and signature checks
- Go to Dev space → Webhooks → Webhook manager → Create webhook.
- Receiver: HTTP address; target: your HTTPS endpoint (Sumsub does not send to plain HTTP).
- Types: applicantReviewed at minimum; applicantPending and applicantOnHold are useful for status.
- Applicant types: individuals and companies. Signature algorithm SHA256; keep the secret key in your secret manager.
- Answer within 10 seconds (accept the connection within 1 second); failed deliveries are retried.
sequenceDiagram
participant S as Sumsub
participant W as Your webhook endpoint
participant K as KYC records
participant L as App logs
S->>W: POST applicantReviewed + x-payload-digest
W->>W: HMAC of the raw body = digest?
alt digest does not match
W-->>S: 401
else digest matches
W->>S: GET applicant
S-->>W: data, documents, answers
W->>K: append status + Armenian fields (hash-chained)
W->>L: summary only - status, ids, no personal data
W-->>S: 200
endimport { verifyWebhookDigest, parseWebhook, toDictionary } from "kyc-am/sumsub";
if (!verifyWebhookDigest(rawBody, req.headers, process.env.SUMSUB_WEBHOOK_SECRET)) return res.status(401).end();
const hook = parseWebhook(rawBody);
const applicant = await sumsub.getApplicant(hook.applicantId);
const { values, gaps } = toDictionary(applicant, { level: "standard" });
// gaps → e.g. [{ field: "place_of_birth", rules: ["AML-16.4.1"], howToFill: "…" }]No public web address? Open Dram's desktop app has none, so it uses Fetch status on the user page instead of a webhook. The result is mapped the same way.
Records, reliance and retention
Relying on Sumsub does not move responsibility. Under AML Art. 16(8) the responsibility stays with you, and you must be able to produce the identification data and copies of documents immediately on request. Decision 12-N (point 4) asks you to keep the customer data and a copy of the identity document for at least five years, and point 30 gives five working days to answer the Central Bank.
const images = await sumsub.listDocumentImages(applicantId);
for (const img of images) {
const { bytes, contentType } = await sumsub.downloadDocumentImage(applicantId, img.id);
await yourVault.put(`kyc/${customerId}/${img.id}`, bytes, contentType);
}flowchart LR
A["Relationship starts"] --> B["Identity verified<br/>within 7 days (Art. 16(1))"]
B --> C["Data refreshed<br/>at least yearly (Art. 17(2))"]
C --> D["Relationship ends"]
D --> E["Keep identification data,<br/>document copy, BO reasoning<br/>5 years (Art. 22, 12-N p.4)"]
T["Each transaction"] --> F["Keep transaction data<br/>5 years from its date<br/>(Art. 22, 12-N p.7)"]
V["Cash-desk video"] --> G["Keep 6 months<br/>(12-N p.12)"]
E --> H["Then erase or anonymise<br/>(Personal Data Law Art. 5)"]
F --> H- Retention runs from the end of the relationship for customer data and from the transaction date for transactions — whichever is later when both apply.
- Keep longer when a request, an investigation or another law requires it.
- Sumsub's own retention settings are a separate question: your obligation is met by your records, not by the processor's.
Connect: Open Dram or your own backend
- Open Dram desktop: Settings → Integrations · Sumsub → Connect Sumsub, paste the token and secret, set the level name am-individual-standard, and make Sumsub the default KYC method. The keys stay in your computer's keychain.
- Your own backend: the open-source kyc-am module (Apache-2.0) gives you the Armenian data dictionary in three languages, CDD validation, retention dates, a hash-chained KYC log with log-safe summaries, Decision 12-N record shapes and this Sumsub adapter. Every rule in it links to its article.
Test in Sandbox, then go live
POST /resources/applicants/{applicantId}/status/testCompleted
{ "reviewAnswer": "GREEN", "rejectLabels": [] }
{ "reviewAnswer": "RED", "reviewRejectType": "RETRY",
"rejectLabels": ["UNSATISFACTORY_PHOTOS"] } // → needs documents
{ "reviewAnswer": "RED", "reviewRejectType": "FINAL",
"rejectLabels": ["FORGERY"] } // → rejected- A GREEN applicant maps to approved; RED + RETRY to needs documents; RED + FINAL to rejected.
- Run an Armenian passport, an Armenian ID card and a foreign passport; check that no required field is left in the gap list.
- Send a webhook with a wrong signature and make sure it is refused.
- Check your logs: no names, document numbers, addresses or emails.
- Create Production tokens and a Production webhook — Sandbox tokens do not work there — and rerun one real verification with your own documents.
- Put the next review date (at most a year) and the retention dates in your system from day one.
