Authentication
Registration, login, token refresh, logout, password reset, identifiers, and device registration.
YCMT-EU Developer Package — Authentication Guide
Version: v1.0 draft
API host: https://api.yes.cash
Audience: Partner technical teams building customer-facing integration apps.
---
1. Purpose
This guide explains how the partner customer app integrates with the YCMT-EU Authentication API.
Use this guide to implement:
- customer registration;
- customer login;
- token refresh;
- logout;
- password reset;
- customer identifier management;
- device registration for transfer confirmation.
The Authentication API is exposed under:
https://api.yes.cash/v1/auth
---
2. Authentication model
The partner app presents the customer-facing UX and calls the YCMT-EU API.
YCMT-EU returns access and refresh tokens after successful registration or login. The partner app then uses the access token to call customer-authenticated API endpoints.
Basic model:
Customer enters credentials in partner app
-> Partner app calls YCMT Auth API
-> YCMT Auth API returns accessToken + refreshToken
-> Partner app calls YCMT Core API with Authorization: Bearer <accessToken>
The partner app must treat tokens as bearer credentials.
Do:
- store tokens securely;
- refresh access tokens before or after expiry;
- delete local tokens on logout;
- pass the access token in the
Authorizationheader.
Do not:
- use token claims for authorization decisions;
- expose tokens in logs;
- send tokens to the partner backend unless explicitly required by the approved integration design;
- store tokens in insecure browser storage.
---
3. Required headers
3.1 Public Auth calls
The following endpoints do not require a bearer token:
POST /v1/auth/start
POST /v1/auth/login
POST /v1/auth/register/start
POST /v1/auth/register/verify-otp
POST /v1/auth/register/set-password
POST /v1/auth/password/forgot
POST /v1/auth/password/reset
Use these headers:
Content-Type: application/json
Accept: application/json
Ocp-Apim-Subscription-Key: <partner-api-subscription-key>
X-Correlation-Id: <uuid-v4>
3.2 Customer-authenticated Auth calls
The following endpoints require a customer access token:
POST /v1/auth/refresh
POST /v1/auth/logout
GET /v1/auth/identifiers
POST /v1/auth/identifiers/add/start
POST /v1/auth/identifiers/add/verify-otp
POST /v1/auth/identifiers/{identifierId}/remove/start
POST /v1/auth/identifiers/{identifierId}/remove/verify-otp
POST /v1/auth/device-registration/start
POST /v1/auth/device-registration/complete
Use these headers:
Content-Type: application/json
Accept: application/json
Authorization: Bearer <accessToken>
Ocp-Apim-Subscription-Key: <partner-api-subscription-key>
X-Correlation-Id: <uuid-v4>
---
4. Identifier format
The Auth API supports two customer identifier types:
| Type | Format | Example |
|---|---|---|
EMAIL | Email address | alice@example.com |
PHONE | E.164 phone number | +34612345678 |
Recommended partner-app behavior:
- ask the customer whether they are using email or phone;
- send
identifierTypeexplicitly; - normalise email to lowercase before sending;
- send phone numbers in E.164 format.
Example:
{
"identifier": "alice@example.com",
"identifierType": "EMAIL"
}
---
5. Login flow
Login is a two-step flow.
POST /v1/auth/start
POST /v1/auth/login
5.1 Step 1 — Start login
Endpoint:
POST https://api.yes.cash/v1/auth/start
Request:
{
"identifier": "alice@example.com",
"identifierType": "EMAIL"
}
Successful response:
{
"loginAttemptId": "LA-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"loginAttemptExpiresAt": "2026-05-11T14:10:12Z"
}
Partner-app behavior:
- store
loginAttemptIdtemporarily; - move the customer to the password screen;
- do not show whether the identifier exists or not;
- restart the login if
loginAttemptExpiresAtpasses.
5.2 Step 2 — Complete login
Endpoint:
POST https://api.yes.cash/v1/auth/login
Request:
{
"loginAttemptId": "LA-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"password": "AliceStr0ngP@ssw0rd!"
}
Successful authenticated response:
{
"authStatus": "AUTHENTICATED",
"accessToken": "<accessToken>",
"refreshToken": "<refreshToken>",
"tokenType": "Bearer",
"expiresIn": 3600,
"accessTokenExpiresAt": "2026-05-11T15:00:00Z",
"refreshTokenExpiresAt": "2026-06-10T14:00:00Z",
"subscriptionId": "SUB-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"customerStatus": "PENDING"
}
Partner-app behavior:
- store
accessTokenandrefreshTokensecurely; - store
subscriptionIdas the partner-facing customer reference; - use
customerStatusto decide the next UX step; - call Core API using
Authorization: Bearer <accessToken>.
---
6. MFA outcomes during login
The /v1/auth/login response is polymorphic. The partner app must support all documented authStatus values.
authStatus | Meaning | Partner-app action |
|---|---|---|
AUTHENTICATED | Login succeeded | Store tokens and continue. |
MFA_CHALLENGE_REQUIRED | Customer must enter MFA code | Ask for the MFA code and call /v1/auth/login again. |
MFA_ENROLMENT_REQUIRED | Customer must enrol MFA | Show supported methods, complete enrolment, then call /v1/auth/login again. |
6.1 MFA challenge required
Example response:
{
"authStatus": "MFA_CHALLENGE_REQUIRED",
"mfaChallengeId": "MFC-01HX9J5Q1R7S9T1U3V5W7X9Y1Z",
"mfaMethod": "TOTP",
"mfaChallengeExpiresAt": "2026-05-11T14:05:00Z"
}
Follow-up request:
{
"loginAttemptId": "LA-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"mfaChallengeId": "MFC-01HX9J5Q1R7S9T1U3V5W7X9Y1Z",
"mfaMethod": "TOTP",
"mfaCode": "847291"
}
6.2 MFA enrolment required
Example response:
{
"authStatus": "MFA_ENROLMENT_REQUIRED",
"mfaEnrolmentSessionId": "MFE-01HX9K7S2T9U1V3W5X7Y9Z1A3B",
"supportedMethods": ["TOTP", "SMS_MFA"],
"mfaEnrolmentSessionExpiresAt": "2026-05-11T14:05:00Z"
}
Follow-up request:
{
"loginAttemptId": "LA-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"mfaEnrolmentSessionId": "MFE-01HX9K7S2T9U1V3W5X7Y9Z1A3B",
"mfaEnrolmentMethod": "TOTP",
"mfaEnrolmentCode": "319847"
}
---
7. New customer registration flow
Registration is a three-step flow.
POST /v1/auth/register/start
POST /v1/auth/register/verify-otp
POST /v1/auth/register/set-password
After registration succeeds, the partner app should prompt the customer to register their device.
7.1 Step 1 — Start registration
Endpoint:
POST https://api.yes.cash/v1/auth/register/start
Request:
{
"identifier": "alice@example.com",
"identifierType": "EMAIL",
"partnerCustomerRef": "PARTNER-ALICE-001"
}
partnerCustomerRef is optional. Use it only as your own opaque reference for reconciliation or support.
Successful response:
{
"registrationId": "RG-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"next": "OTP",
"registrationIdExpiresAt": "2026-05-11T14:35:12Z"
}
Partner-app behavior:
- ask the customer to enter the OTP sent through the selected channel;
- keep
registrationIdtemporarily; - restart registration if the session expires.
7.2 Step 2 — Verify registration OTP
Endpoint:
POST https://api.yes.cash/v1/auth/register/verify-otp
Request:
{
"registrationId": "RG-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"otp": "123456"
}
Successful response:
{
"registrationId": "RG-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"branch": "NEW_CUSTOMER",
"next": "SET_PASSWORD"
}
Possible branch values:
branch | Meaning | Partner-app action |
|---|---|---|
NEW_CUSTOMER | Identifier belongs to a new customer path | Ask the customer to set a password. |
EXISTING_CUSTOMER | Identifier belongs to an existing customer path | Ask the customer to verify their password or follow the returned next step. |
7.3 Step 3 — Set password
Endpoint:
POST https://api.yes.cash/v1/auth/register/set-password
Request:
{
"registrationId": "RG-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"password": "AliceStr0ngP@ssw0rd!"
}
Successful response:
{
"accessToken": "<accessToken>",
"refreshToken": "<refreshToken>",
"tokenType": "Bearer",
"expiresIn": 3600,
"accessTokenExpiresAt": "2026-05-11T15:00:00Z",
"refreshTokenExpiresAt": "2026-06-10T14:00:00Z",
"subscriptionId": "SUB-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"customerStatus": "PENDING"
}
Partner-app behavior:
- store tokens securely;
- store
subscriptionIdas the partner-facing customer reference; - continue to device registration;
- then continue to profile and KYC.
---
8. Token refresh
Access tokens are short-lived. Use refresh when the access token is near expiry or after receiving auth.tokenExpired.
Endpoint:
POST https://api.yes.cash/v1/auth/refresh
Headers:
Authorization: Bearer <accessToken>
Ocp-Apim-Subscription-Key: <partner-api-subscription-key>
X-Correlation-Id: <uuid-v4>
Request:
{
"refreshToken": "<refreshToken>"
}
Successful response:
{
"accessToken": "<newAccessToken>",
"refreshToken": "<newRefreshToken>",
"tokenType": "Bearer",
"expiresIn": 3600,
"accessTokenExpiresAt": "2026-05-11T16:00:00Z"
}
Partner-app behavior:
- replace the stored access token;
- replace the stored refresh token if a new one is returned;
- retry the original customer action after a successful refresh;
- if refresh fails, clear local tokens and ask the customer to log in again.
---
9. Logout
Logout revokes the current refresh token and ends the local customer session.
Endpoint:
POST https://api.yes.cash/v1/auth/logout
Request:
{
"refreshToken": "<refreshToken>"
}
Successful response:
204 No Content
Partner-app behavior:
- call
/v1/auth/logoutwhere possible; - delete local access and refresh tokens;
- clear sensitive customer session state;
- redirect the customer to the logged-out screen.
If logout cannot be completed because the network is unavailable, the app must still delete local tokens.
---
10. Password reset
Password reset is a two-step flow.
POST /v1/auth/password/forgot
POST /v1/auth/password/reset
10.1 Step 1 — Start password reset
Endpoint:
POST https://api.yes.cash/v1/auth/password/forgot
Request:
{
"identifier": "alice@example.com",
"identifierType": "EMAIL"
}
Successful response:
{
"passwordResetId": "PR-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"next": "OTP",
"passwordResetIdExpiresAt": "2026-05-11T14:35:12Z"
}
Partner-app behavior:
- ask the customer to enter OTP;
- do not reveal whether the identifier exists;
- continue to
/password/reset.
10.2 Step 2 — Complete password reset
Endpoint:
POST https://api.yes.cash/v1/auth/password/reset
Request:
{
"passwordResetId": "PR-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"otp": "123456",
"newPassword": "NewStr0ngP@ssw0rd!"
}
Successful standalone response:
{
"next": "LOGIN"
}
Partner-app behavior:
- ask the customer to log in with the new password;
- clear any existing local tokens for this customer.
---
11. Identifier management
Identifier management requires an authenticated customer session.
11.1 List identifiers
Endpoint:
GET https://api.yes.cash/v1/auth/identifiers
Successful response:
{
"identifiers": [
{
"identifierId": "ID-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"identifierType": "EMAIL",
"identifierMasked": "a***e@example.com",
"verifiedAt": "2026-05-11T14:00:00Z",
"canRemove": true
}
]
}
Partner-app behavior:
- display masked identifiers;
- allow removal only when
canRemoveistrue; - never display raw identifiers unless entered by the customer in the current screen.
11.2 Add identifier
Step 1:
POST https://api.yes.cash/v1/auth/identifiers/add/start
Request:
{
"identifier": "+34612345678",
"identifierType": "PHONE"
}
Successful response:
{
"identifierAddId": "IAD-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"next": "OTP",
"identifierAddIdExpiresAt": "2026-05-11T14:35:12Z"
}
Step 2:
POST https://api.yes.cash/v1/auth/identifiers/add/verify-otp
Request:
{
"identifierAddId": "IAD-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"otp": "123456"
}
Partner-app behavior:
- refresh the identifier list after success;
- show only masked values in account settings.
11.3 Remove identifier
Step 1:
POST https://api.yes.cash/v1/auth/identifiers/{identifierId}/remove/start
Request:
{
"otpDeliveryIdentifierId": "ID-01HX9F2J7K3M5N7P9Q1R3T5V7W"
}
Successful response:
{
"identifierRemoveId": "IRD-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"next": "OTP",
"otpDeliveryIdentifierMasked": "a***e@example.com",
"identifierRemoveIdExpiresAt": "2026-05-11T14:35:12Z"
}
Step 2:
POST https://api.yes.cash/v1/auth/identifiers/{identifierId}/remove/verify-otp
Request:
{
"identifierRemoveId": "IRD-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"otp": "123456"
}
Successful response:
{
"identifierId": "ID-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"deactivatedAt": "2026-05-11T14:22:00Z"
}
Partner-app behavior:
- do not allow the customer to remove the last usable identifier;
- rely on the
canRemoveflag from the list endpoint; - refresh the list after successful removal.
---
12. Device registration
Device registration is required before the customer can complete device-bound transfer confirmation.
Device registration requires an authenticated customer session.
Flow:
POST /v1/auth/device-registration/start
POST /v1/auth/device-registration/complete
The detailed JWS construction is covered in 08_Device_Bound_Authentication.md. This section explains the Auth API flow only.
12.1 Start device registration
Endpoint:
POST https://api.yes.cash/v1/auth/device-registration/start
Request:
{
"deviceMetadata": {
"deviceModel": "iPhone 15",
"osVersion": "iOS 18.4",
"appVersion": "1.0.0",
"platform": "iOS"
}
}
Successful first-device response:
{
"deviceRegistrationId": "DRG-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"registrationChallenge": "Y2hhbGxlbmdlLWJhc2U2NHVybC1lbmNvZGVkLTMyLWJ5dGVz",
"expiresAt": "2026-05-11T15:13:22Z",
"publicKeyRequirements": {
"algorithm": "RS256",
"minimumModulusBits": 2048,
"recommendedModulusBits": 3072
},
"stepUpApplied": false
}
Partner-app behavior:
- generate an RSA key pair on the customer device;
- keep the private key on the device;
- build a public JWK from the public key;
- sign the registration challenge to produce
registrationProof; - call
/device-registration/completebeforeexpiresAt.
12.2 Step-up required response
If an active device already exists, the API may require a liveness step-up before replacing it.
Response:
{
"stepUpRequired": "LIVENESS_CHECK",
"kycSessionId": "KYC-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"livenessWebviewUrl": "https://api.yes.cash/webviews/kyc/KYC-01HX...",
"expiresAt": "2026-05-11T15:13:22Z",
"nextStep": "OPEN_LIVENESS_WEBVIEW"
}
Partner-app behavior:
- open the returned liveness webview;
- collect the returned
stepUpTokenafter successful webview completion; - call
/device-registration/startagain withstepUpToken.
Retry request:
{
"stepUpToken": "<stepUpToken>",
"deviceMetadata": {
"deviceModel": "iPhone 15",
"osVersion": "iOS 18.4",
"appVersion": "1.0.0",
"platform": "iOS"
}
}
12.3 Complete device registration
Endpoint:
POST https://api.yes.cash/v1/auth/device-registration/complete
Request:
{
"deviceRegistrationId": "DRG-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"publicKey": {
"kty": "RSA",
"n": "<base64url modulus>",
"e": "AQAB",
"kid": "device-key-2026-05-11",
"alg": "RS256",
"use": "sig"
},
"registrationProof": "<compact-jws>"
}
Successful response:
{
"deviceId": "DEV-01HX9F2J7K3M5N7P9Q1R3T5V7W",
"registeredAt": "2026-05-11T15:10:00Z",
"publicKeyThumbprint": "8mAhQjJyZmJVZkRDeXBKVnpQUFhMQ3JJVnFKUzRSdTk",
"stepUpApplied": false,
"priorDeviceIdDeactivated": null
}
Partner-app behavior:
- store
deviceIdlocally; - keep the private key in secure device storage;
- use the private key later to sign transfer confirmation assertions;
- never export or transmit the private key.
---
13. Token and session storage guidance
13.1 Mobile apps
Recommended storage:
| Item | Recommended storage |
|---|---|
| Access token | Secure storage / keychain-backed storage |
| Refresh token | Secure storage / keychain-backed storage |
| Device private key | OS keychain / secure enclave / Android Keystore where available |
subscriptionId | App secure profile/session storage |
deviceId | App secure profile/session storage |
13.2 Web apps
Recommended behavior:
- prefer secure, httpOnly, same-site cookies where the integration design supports it;
- avoid localStorage for long-lived tokens;
- clear tokens on logout;
- avoid including tokens in URLs;
- never log tokens.
---
14. Standard error envelope
All Auth API errors use the standard error envelope.
Example:
{
"error": {
"code": "auth.tokenExpired",
"message": "The bearer token has expired.",
"correlationId": "c3f1a8b2-4d6e-4f0a-9c1b-2e8d7a6b5c4d",
"details": {}
}
}
Partner-app behavior:
- branch on
error.code, notmessage; - log
correlationIdfor support; - translate customer-facing messages in the partner UX;
- follow endpoint-specific remediation guidance.
Common Auth API errors:
| Error code | Typical meaning | Partner-app action |
|---|---|---|
auth.tokenExpired | Access token expired | Call /v1/auth/refresh. |
auth.tokenInvalid | Token cannot be used | Clear session and ask customer to log in. |
auth.tokenRevoked | Token was revoked | Clear session and ask customer to log in. |
auth.credentialMismatch | Login credentials cannot be accepted | Show generic invalid-login message. |
auth.loginAttemptExpired | Login attempt expired | Restart login. |
auth.registrationSessionExpired | Registration session expired | Restart registration. |
auth.otpInvalid | OTP is incorrect | Ask customer to retry, respecting attempts remaining if returned. |
auth.otpExpired | OTP expired | Restart the OTP step. |
auth.otpAttemptsExhausted | Too many OTP attempts | Restart the relevant flow or show support path. |
validation.passwordPolicyViolation | Password does not meet policy | Show password policy feedback. |
device.registrationRequired | Device not registered | Start device registration. |
device.registrationChallengeExpired | Device challenge expired | Restart device registration. |
rate.limited | Too many requests | Back off and retry later. |
internal.unavailable | Temporary service problem | Show retry message and preserve customer input where safe. |
---
15. Implementation checklist
Before moving to Core API integration, confirm that the partner app can:
- call
/v1/auth/start; - call
/v1/auth/loginand handle allauthStatusoutcomes; - securely store and refresh tokens;
- call
/v1/auth/logoutand clear local session state; - register a new customer;
- complete OTP verification;
- complete password reset;
- list, add, and remove identifiers;
- start and complete device registration;
- handle token expiry and session loss;
- log
X-Correlation-Idand returnederror.correlationId.
---
16. Related package documents
Use these next:
| Document | Purpose |
|---|---|
03_Core_API_Guide.md | Profile, KYC, beneficiaries, quotes, transfers, funding, and transfer status. |
04_End_to_End_Flows.md | Full customer journeys from registration to transfer completion. |
05_Headers_Idempotency_Errors.md | Cross-cutting request headers, idempotency, retries, and errors. |
08_Device_Bound_Authentication.md | Detailed device key and JWS implementation guidance. |
11_Sandbox_and_Testing_Guide.md | Sandbox scenarios and expected test outcomes. |