Skip to content

CustomerRegistrationWithAccount

Endpoint

POST /customer/registration/with-account

Description

Combines into one request what previously took two: CustomerRegistration to create the customer, then OpenCustomerAccount to open an account. It is intended for brand affiliates who register users on their own pages and need a ready-to-use account immediately.

The endpoint accepts everything the plain registration accepts, and requires four fields the plain registration does not: group_name, full_name, phone, and brand. The account is opened in that group; its leverage and currency come from the group, and if the group defines a starting deposit, that amount is credited automatically.

The customer is created in the LEAD lifecycle stage, exactly as in CustomerRegistration. A starting balance does not change that: automatic promotion to FIRST_DEPOSIT is triggered by Cashier transactions, while a group's starting deposit is credited directly.

Authorization

No session is required. This is a public endpoint, rate limited to 10 requests per minute per source IP address.

The brand is taken only from the body brand value, which is therefore required in every request. The HTTP request host is not used to resolve it.

Order of Operations

Everything that can be checked is checked before anything is written. Once the customer record exists, no later failure can undo it.

  1. Schema validation.
  2. Rate limit by source IP.
  3. Request fields are read, then every field a public caller may not set is forced back to its default. See Ignored Fields.
  4. Brand resolved from the brand field. The brand must be enabled.
  5. Group looked up by group_name and checked against the brand's account-opening policy. This is the last point at which the request can be refused without side effects.
  6. Marketing attribution resolved from the request.
  7. Customer created.
  8. Attribution row written, if attribution was resolved.
  9. Account created and published to subscribers.
  10. Starting balance credited, if the group defines one.

Steps 7 to 10 are not a transaction. Customers, accounts, and balances live in independent modules with independent write queues, and there is no cross-module rollback. The order is fixed because an account needs a customer id, so the only possible partial outcome is a customer without an account, never an account without a customer. A customer created in step 7 is never rolled back; the response reports what did and did not happen. See Partial Success.

Request

Minimal, only the six required fields:

{
  "email": "[email protected]",
  "password": "s3cret-pass",
  "group_name": "DEMO_EU",
  "full_name": "Ivan Petrov",
  "phone": "+35700000000",
  "brand": "ALFA"
}

External Landing Attribution

{
  "email": "[email protected]",
  "password": "strong-password",
  "group_name": "DEMO_EU",
  "full_name": "John Smith",
  "phone": "+35700000000",
  "brand": "ALFA",
  "first_name": "John",
  "attribution": {
    "affiliate": "AFF1024",
    "campaign": "FB_CY_01",
    "utm_source": "partner-landing",
    "utm_medium": "affiliate",
    "sub_id": "creative-7"
  }
}

Required

Field Type Limits Description
email string 3-255 Customer email. Must be unique within the installation
password string 6-128 Customer's own password, for the client cabinet. Not the trading account password
group_name string 1-32 Trading group the account is opened in. Must be published by the brand for self-service opening
full_name string ≤ 255 Full display name
phone string ≤ 64 Phone number
brand string ≤ 64 Brand for the customer and the account. The only source of the brand; the request host is not consulted

Optional Personal Data

Field Type Limits Description
first_name string ≤ 128 Given name
last_name string ≤ 128 Family name
middle_name string ≤ 128 Middle name
country_of_residence string ≤ 64 Resolved from the caller's IP by GeoIP when omitted
citizenship string ≤ 64 Citizenship country
preferred_language string ≤ 16 Interface language
timezone string ≤ 64 Timezone label
marketing_allowed int 0-1 Marketing consent

Optional Brand and Attribution

Field Type Limits Description
lead_source string ≤ 128 Acquisition source. Overwritten when attribution resolves
introducer_id string ≤ 128 Introducer reference
source_type int 0-4 0 manual, 1 web, 2 import, 3 affiliate, 4 system. Forced to 3 when an affiliate is resolved
landing_url string ≤ 2048 Landing page
referrer_url string ≤ 2048 Referrer
utm_source string ≤ 255 UTM parameters. Overwritten when attribution resolves
utm_medium string ≤ 255
utm_campaign string ≤ 255
utm_content string ≤ 255
utm_term string ≤ 255
installation_id string ≤ 128 Mobile install identifier, used to match a marketing touch
attribution object Attribution hints for matching a marketing touch
meta_json string ≤ 4000 Free-form JSON stored with the customer

The attribution object and installation_id behave exactly as in CustomerRegistration; see Marketing Attribution API.

Ignored Fields

The endpoint is public, so a whole class of fields is deliberately reset after the request is read. Sending them is not an error: the values are discarded and replaced with defaults.

Field group Forced to
kyc_status, kyc_updated_time, aml_status, aml_updated_time Not verified, AML pending
risk_level, pep_status, sanctions_status Low risk, unknown
email_verified, phone_verified 0
customer_id, external_id Assigned by the server, and empty
lifecycle_stage, status, type, enable Lead, active, individual, enabled
desk, manager_id, assigned_manager_id Unassigned
sales_status, risk_status, finance_status, crm_stage, segment, tags_json Empty
deposit_allowed, withdrawal_allowed 1
tax_id, national_id Empty
enable_otp, otp_secret Disabled
affiliate_id, campaign Empty. Only codes resolved by the marketing module are stored
created_by, updated_by, and all lifecycle timestamps 0

Resetting kyc_status is what makes the endpoint safe to expose. If a caller could declare their own KYC as approved, they would satisfy the brand's kyc_required_for_real gate and open a real-money account without any identity check. Because it is always reset, a freshly registered customer never counts as KYC-approved.

Group Requirements

The group is checked by the same rule that governs self-service account opening in OpenCustomerAccount. Failing any of these refuses the request with 403 before anything is created.

On the Group

Setting Required value If wrong
enable 1 GROUP_DISABLED
public_opening 1 GROUP_NOT_PUBLIC
brand the resolved brand GROUP_BRAND_MISMATCH
account_mode DEMO or REAL UNSUPPORTED_ACCOUNT_MODE
default_leverage greater than 0 Not an error, but the account gets that leverage verbatim, and zero produces a zero-leverage account
default_deposit any Zero means no balance is credited and the account opens empty

On the Brand

Setting Default Effect
can_open_demo 1 Must be 1 for a DEMO group, otherwise DEMO_OPENING_DISABLED
can_open_real 0 Must be 1 for a REAL group, otherwise REAL_OPENING_DISABLED
kyc_required_for_real 1 Must be 0 for a REAL group, otherwise KYC_REQUIRED

Real-money accounts need a deliberate brand decision

A customer created by this endpoint never has KYC. So while kyc_required_for_real stays at its default of 1, a REAL group is refused every time, no matter how the group itself is configured. Opening real accounts here requires the brand to set can_open_real = 1 and kyc_required_for_real = 0, which means real-money accounts opened with no identity verification. That is a compliance decision rather than a settings change. With default settings, this endpoint opens DEMO accounts.

A group's account_mode defaults to REAL. A group created without setting it explicitly is treated as real and refused under default brand settings, which reads as "I marked the group public and it still will not open". Check the mode first.

Response

{
  "registered": true,
  "customer": {
    "customer_id": 1042,
    "email": "[email protected]",
    "lifecycle_stage": 0,
    "status": 0,
    "enable": 1,
    "brand": "ALFA",
    "created_time": 1777600000,
    "updated_time": 1777600000
  },
  "account_created": true,
  "login": 500317,
  "group": "DEMO_EU",
  "brand": "ALFA",
  "account_mode": "DEMO",
  "currency": "USD",
  "leverage": 100,
  "temporary_password": "a3f19c02b7d4",
  "must_change_password": true,
  "balance_credited": true,
  "balance": 10000.0
}
Field Type Description
registered bool Always true once the customer exists
customer object The created customer, same shape as CustomerRegistration returns
account_created bool Whether the trading account was opened
login int Account number. Present only when the account was created
group string Group the account was opened in
brand string Brand of that group
account_mode string DEMO or REAL
currency string Account currency, taken from the group
leverage int Taken from the group's default_leverage
temporary_password string Generated password for the trading account. Returned once and never again
must_change_password bool Always true. The temporary password is meant to be replaced
balance_credited bool Whether the starting balance was credited. false when the group has no starting deposit
balance number Amount credited. Present only when it was

The request carries two different passwords. The password in the request is the customer's own, for the client cabinet. The temporary_password in the response belongs to the trading account and is generated by the server. They are unrelated, and the account password is returned exactly once; it cannot be read back later.

Partial Success

Status 207 means the customer was created but something after that was not. The customer is never rolled back, and the response always says exactly how far the call got.

Customer created, account was not:

{
  "registered": true,
  "customer": { "customer_id": 1042 },
  "account_created": false,
  "error": "RET_ERROR",
  "message": "..."
}

Customer and account created, balance was not:

{
  "registered": true,
  "customer": { "customer_id": 1042 },
  "account_created": true,
  "login": 500317,
  "group": "DEMO_EU",
  "balance_credited": false,
  "error": "ADD_BALANCE_ERROR",
  "message": "..."
}

On the first shape the customer exists and can be given an account later through the normal account-opening flow. On the second the account exists and is usable, and only the starting balance is missing, which a manager can credit. In both cases customer_id is in the response, so nothing is lost.

Do not retry the same call after a 207. The email is already taken, so a retry returns 409.

Errors

HTTP Error Description
400 INVALID_DATA Schema validation failed: a missing required field, a wrong type, or a value outside its limits
400 INVALID_GROUP No group with that name exists
403 GROUP_DISABLED The group is switched off
403 GROUP_NOT_PUBLIC The group is not published for self-service opening
403 GROUP_BRAND_MISMATCH The group belongs to a different brand
403 DEMO_OPENING_DISABLED The brand does not allow self-service demo accounts
403 REAL_OPENING_DISABLED The brand does not allow self-service real accounts
403 KYC_REQUIRED The brand requires KYC for real accounts, which a new registration cannot have
403 UNSUPPORTED_ACCOUNT_MODE The group's account mode is neither demo nor real
403 BRAND_NOT_AVAILABLE The brand is disabled
404 BRAND_NOT_FOUND The brand value does not match an existing brand
409 RET_DUPLICATE_RECORD A customer with this email already exists
429 RET_TOO_FREQUENT More than 10 requests in a minute from one IP address
500 CORE_NOT_AVAILABLE The trading core is not ready to accept account creation

All nine group and brand refusals return their own code rather than a generic message, so an integrator can tell a misconfigured group from a brand policy from a missing KYC. Every status up to and including 429 is returned before anything is created: nothing was written, and the call can be corrected and repeated.

Integration Notes

  • Rate limit. 10 requests per minute per IP address. Exceeding it returns 429 with nothing created. The limit is per source address, so several affiliates behind one gateway share the budget.
  • The brand comes only from the body. Unlike CustomerRegistration, this endpoint does not derive the brand from the request host, so brand must be sent explicitly and must name an enabled brand.
  • Country is filled from the IP. If country_of_residence is omitted, GeoIP resolves it from the caller's address.
  • Attribution overrides what you send. If the marketing module matches a touch, by cookies, install identifier, or the attribution object, the resolved affiliate, campaign, source, and UTM values replace whatever the request contained, and source_type becomes affiliate. This is what credits the partner with the registration.
  • The customer is created as a lead. Promotion to a later lifecycle stage is a manager or workflow action.
  • A starting balance does not change the lifecycle stage. Automatic promotion to first deposit is triggered by Cashier transactions; a group's starting deposit is credited directly and leaves the customer at lead.
  • Every call is written to the audit log.