BizKitHub
DocsAPI ReferenceVendor/api/v1/vendor/create-organisation
postVendorPublic API v1

/api/v1/vendor/create-organisation

Creates a new BizKitHub organisation on behalf of a white-label partner. The new org's parent_id is set to the caller (the vendor), so it appears in the vendor's /vendor/managed-organisations list and the customer-side "Můj technický správce" widget points back to the vendor. The founder is provisioned as a fresh cas__user + shop__contact in the same call and becomes the root member of the new org.

Licence gate: the calling organisation MUST hold an active row in cas__white_label_licence (see hasWhiteLabelLicence). Calls from non-licensed orgs are rejected with NOT_LICENSED — this is what prevents random API keys from spawning parented orgs.

Welcome e-mail: the platform welcome (organisation-register template) is always sent from INTERNAL_ORGANISATION_ID (BizKitHub sender), NOT from the vendor. If you want to send your own branded welcome on top, do it from your own transactional sender after the call succeeds.

Idempotency: the endpoint is NOT idempotent. Retrying a successful call will attempt to create a second organisation and fail with EMAIL_ALREADY_TAKEN on the founder step. Persist the returned organisationId on the vendor side before retrying.

List of error codes:

Code Message
NOT_LICENSED Calling organisation does not hold an active white-label licence. Contact BizKitHub to request one.
EMAIL_ALREADY_TAKEN A BizKitHub user account already exists for this e-mail. The founder must use a fresh address.
WEAK_PASSWORD Password does not meet the minimum strength requirements.
HIGH_RISK Request temporarily unavailable from this network. Try again later or contact support.
vendorpostApiV1VendorCreate-organisation

Parameters

1 query · JSON body

Query parameters

· 1
apiKeystringRequired

Your BizKitHub API key (passed as GET parameter).

Key format: A 32-character string matching: ^(PROD|DEV_|ROOT)[A-Za-z0-9]{28}$
Prefixes: PROD (production key), DEV_ (individual developer), ROOT (system key with no limits). Learn more

ExamplePRODPGrFxpGEtrOZfuWhnoJohUYBXuOE

Request body

application/json
organisationNamestringRequired

Display name of the new organisation the vendor is spawning. Must be unique across the platform; if the exact name is already taken, a numeric suffix is appended automatically (e.g. "Acme" → "Acme 2").

Length: 1–128
ExampleAcme Shop
emailstringRequired

Contact email address.

The system validates the input as a standard email address and automatically applies normalization and canonicalization.

All API responses return the normalized form, and each email address is unique per organisation within the system.

Phone-only contacts: Since 2026-06-10 a contact may exist without an e-mail when it was registered only by phone (e.g. imports of phone-only records). Responses that expose such contacts use API_EMAIL_NULLABLE instead, where this field can be null. Endpoints that accept e-mail as input still require a valid value here — phone-only creation goes through admin-only import / BFF flows.

Vendor spawn: address of the customer who will become the founder + root member of the new organisation. Must NOT already resolve to an existing cas__user — reuse of an existing account is rejected with EMAIL_ALREADY_TAKEN (the customer would have two root accounts otherwise). If your customer already has a BizKitHub account, invite them as a member of an existing org instead of spawning a new one.

Examplejan@barasek.com
passwordstringRequired

Founder password (min 8 chars, standard strength check). Collect it from the vendor form; BizKitHub never emails the plaintext back. If you prefer a "reset link" flow, send any placeholder here and immediately call POST /api/v1/customer/request-reset-password for the same e-mail to issue a token the customer can use to set their real password.

Length: 8–∞
firstNamestringRequired

Founder first name — becomes the contact's first name in the new org.

Length: 1–128
lastNamestringRequired

Founder last name — becomes the contact's last name in the new org.

Length: 1–128
localestringOptional

Communication locale code — controls the language of textual data (product names, descriptions, articles, storefront UI, transactional e-mails).

Preferred format: BCP 47 language tag — language[-Script][-REGION]. Use the full tag whenever the script or region matters:

  • en-GB vs. en-US (British vs. American spelling)
  • pt-PT vs. pt-BR (European vs. Brazilian Portuguese)
  • zh-Hans vs. zh-Hant (Simplified vs. Traditional Chinese)
  • sr-Latn vs. sr-Cyrl (Latin vs. Cyrillic Serbian)

Backwards-compatible fallback: the bare two-letter ISO 639-1 code (cs, en, pl, …) is accepted indefinitely — legacy clients that only send the language subtag continue to work unchanged.

Resolution algorithm (server-side): the input is resolved against the supported locale list via the [RFC 4647 Lookup] progressive-fallback strategy — trailing subtags are stripped one by one until a supported locale is found. Example: en-GB-oxendict → en-GB → en (matched). If no subtag combination is supported, the request is rejected.

Currently supported locales: cs, en, fr, it, pl, de, sk, sv, es, zh, ja, uk, da, hu, ro, nl, pt, fi, nb, hr. Region-specific variants (e.g. en-GB, pt-BR) are accepted and resolved to their base language when the exact variant is not registered separately.

Defaults to cs when omitted. Drives the primary locale of the new org, the founder's UI language, and the language of the platform welcome e-mail.

Length: 2–35
Examplescsenen-GBpt-BRzh-Hans
timezonestringOptional

IANA timezone identifier for the new org (e.g. Europe/Prague). When omitted, resolved from the request IP's geo record.

ExampleEurope/Prague
countryIdnumberOptional

HQ country FK (core__country.id, from GET /organisation/available-countries). When omitted, resolved via geo lookup with Czech Republic (id=158) as ultimate fallback.

Example158
descriptionstringOptional

Optional short description of the new organisation.

Length: 0–512
companyRegistrationNumberstringOptional

Optional company registration number (IČO in CZ). When provided, an ARES lookup is attempted to pre-fill legal name, address, and tax data.

taxIdentificationNumberstringOptional

Optional VAT / tax identification number (DIČ in CZ).

checkboxTermsbooleanOptional

Whether the founder has agreed to BizKitHub's terms and conditions. The vendor is responsible for collecting this consent from the end customer before calling the API.

Default: false
checkboxMarketingbooleanOptional

Whether the founder has opted in to marketing communication.

Default: true
customerRealIpstringOptional

Accepted formats:

  • IPv4 dot-decimal, e.g. 1.1.1.1 (4 octets, 0–255, no leading zeros).
  • IPv6 as defined by RFC 4291 — full 2001:0db8:0000:0000:0000:0000:0000:0001, zero-compressed 2001:db8::1, IPv4-mapped ::ffff:1.2.3.4, or scoped literals. Both upper- and lower-case hex are accepted.

Server-side canonicalization (ipNormalize in core/src/lib/network/ipNormalize.ts):

  • Valid IPv4 is passed through verbatim.
  • Valid IPv6 is lowercased (RFC 5952 §4.3).
  • IPv4-mapped IPv6 ::ffff:X.X.X.X is unwrapped to plain IPv4 (RFC 4291 §2.5.5.2) so 1.2.3.4 and ::ffff:1.2.3.4 share one brj__geo_ip row.
  • Loopback aliases (::1, 0.0.0.0, localhost, empty string) collapse to 127.0.0.1.
  • Junk values that fail both IPv4 and IPv6 validation are silently rejected and replaced with 127.0.0.1 (loopback).

On the wire: every response returns the canonicalized form — clients can safely rely on lowercase IPv6 and the plain-IPv4 unwrap when de-duping or joining. Server-originated writers (activity log, session log, ban list) resolve the visitor IP via resolveClientIp / resolveClientIpOrNull — always native IPv6 on Vercel Edge (there is no auto-mapping to ::ffff:X.X.X.X).

Enrichment: the system resolves reverse DNS, geolocation, ASN, mobile/proxy/hosting/Tor flags via our VikiTron GEO/IP resolver for both address families. Learn more

Examples1.1.1.12001:4860:4860::8888

Response schema

1 status code documented

200Success
object | object
One of 2:
Variant 1
success"true"Required
organisationIdnumberRequired

Internal id of the newly created organisation. Use this to correlate on the vendor side.

organisationSlugstringRequired

URL slug of the new organisation. Use with POST /vendor/switch to enter it as a member.

Exampleacme-shop
founderMemberIdnumberRequired

cas__organisation_member.id of the founder (root member) inside the new organisation.

founderEmailstringRequired

Normalised e-mail address stored for the founder account (may differ in casing from input).

riskScorestring | integerRequired

Server-computed risk score for the current request, on an integer scale 0 – 100 (RISK_SCORE_MIN / RISK_SCORE_MAX in features/security/riskScore/clampScore.ts).

Direction: higher = riskier. 0 is a clean, fully trusted signal; 100 is a hard-block-worthy signal (TOR exit node, active abuse-IP feed hit, login-flood threshold breached, or the caller organisation has explicitly banned the visitor IP).

Recommended integration buckets for the calling application — these are guidance, not a contract, callers are free to pick their own thresholds:

Bucket Range What it means Suggested action
trusted 0 – 19 Green — known device / prior success / same-country / EU IP for an EU org. Proceed silently.
neutral 20 – 49 No strong signal either way (fresh IP, unknown device, no history). Proceed, log the score, optionally show a soft "verify e-mail" nudge.
suspicious 50 – 74 Multiple soft red flags stacked (foreign origin for an EU org, new IP, other-org ban, …). Require a second factor before granting sensitive actions (MFA, magic-link re-verification, CAPTCHA, ID verification).
hostile 75 – 100 Hard-block territory. Above 75 the internal enforceLoginRiskScore throws LoginRiskScoreRejectionError; 100 is reached only via a hard-block signal (TOR / active abuse-IP / own-org ban / login flood). Reject the follow-up action, force password reset / step-up auth.

How it is computed: the score is a weighted average of independent signal families (IP reputation, org-country match, EU/high-risk-country policy, per-user history), clamped to [0, 100]. Any single family may return hardBlock(...) to short-circuit the average to 100. Green signals (prior successful login from this IP, paid orders, active session, mobile carrier) subtract from the total; red signals (new IP, other-org ban, foreign continent for an EU org) add to it. Full rule table lives in features/security/riskScore/calculateIpRiskScore.ts.

Stability: the numeric value is a heuristic — the exact algorithm may evolve between releases as new signals are added. The scale, direction and the four bucket boundaries above are stable; integrate against buckets, not exact numbers.

Order-create note: the endpoint currently returns the best-case score (0) for order creation as a placeholder — the order-side risk model is being built out separately (device fingerprint, cart velocity, geo/billing mismatch, chargeback history). The field is exposed already so downstream integrations can wire their step-up logic once and pick up richer values automatically when the order-side model ships.

One of 2:
Variant 1
stringinteger
Default: 0
Variant 2
integer

Server-computed risk score for the current request, on an integer scale 0 – 100 (RISK_SCORE_MIN / RISK_SCORE_MAX in features/security/riskScore/clampScore.ts).

Direction: higher = riskier. 0 is a clean, fully trusted signal; 100 is a hard-block-worthy signal (TOR exit node, active abuse-IP feed hit, login-flood threshold breached, or the caller organisation has explicitly banned the visitor IP).

Recommended integration buckets for the calling application — these are guidance, not a contract, callers are free to pick their own thresholds:

Bucket Range What it means Suggested action
trusted 0 – 19 Green — known device / prior success / same-country / EU IP for an EU org. Proceed silently.
neutral 20 – 49 No strong signal either way (fresh IP, unknown device, no history). Proceed, log the score, optionally show a soft "verify e-mail" nudge.
suspicious 50 – 74 Multiple soft red flags stacked (foreign origin for an EU org, new IP, other-org ban, …). Require a second factor before granting sensitive actions (MFA, magic-link re-verification, CAPTCHA, ID verification).
hostile 75 – 100 Hard-block territory. Above 75 the internal enforceLoginRiskScore throws LoginRiskScoreRejectionError; 100 is reached only via a hard-block signal (TOR / active abuse-IP / own-org ban / login flood). Reject the follow-up action, force password reset / step-up auth.

How it is computed: the score is a weighted average of independent signal families (IP reputation, org-country match, EU/high-risk-country policy, per-user history), clamped to [0, 100]. Any single family may return hardBlock(...) to short-circuit the average to 100. Green signals (prior successful login from this IP, paid orders, active session, mobile carrier) subtract from the total; red signals (new IP, other-org ban, foreign continent for an EU org) add to it. Full rule table lives in features/security/riskScore/calculateIpRiskScore.ts.

Stability: the numeric value is a heuristic — the exact algorithm may evolve between releases as new signals are added. The scale, direction and the four bucket boundaries above are stable; integrate against buckets, not exact numbers.

Order-create note: the endpoint currently returns the best-case score (0) for order creation as a placeholder — the order-side risk model is being built out separately (device fingerprint, cart velocity, geo/billing mismatch, chargeback history). The field is exposed already so downstream integrations can wire their step-up logic once and pick up richer values automatically when the order-side model ships.

Range: 0–100
Examples02560100
Variant 2
success"false"Required
errorCodestringRequired
ExamplesNOT_LICENSEDEMAIL_ALREADY_TAKENWEAK_PASSWORDHIGH_RISK
messagestringRequired
ExamplesCalling organisation does not hold an active white-label licence. Contact BizKitHub to request one.A BizKitHub user account already exists for this e-mail. The founder must use a fresh address.Password does not meet the minimum strength requirements.Request temporarily unavailable from this network. Try again later or contact support.
riskScorestring | integerRequired

Server-computed risk score for the current request, on an integer scale 0 – 100 (RISK_SCORE_MIN / RISK_SCORE_MAX in features/security/riskScore/clampScore.ts).

Direction: higher = riskier. 0 is a clean, fully trusted signal; 100 is a hard-block-worthy signal (TOR exit node, active abuse-IP feed hit, login-flood threshold breached, or the caller organisation has explicitly banned the visitor IP).

Recommended integration buckets for the calling application — these are guidance, not a contract, callers are free to pick their own thresholds:

Bucket Range What it means Suggested action
trusted 0 – 19 Green — known device / prior success / same-country / EU IP for an EU org. Proceed silently.
neutral 20 – 49 No strong signal either way (fresh IP, unknown device, no history). Proceed, log the score, optionally show a soft "verify e-mail" nudge.
suspicious 50 – 74 Multiple soft red flags stacked (foreign origin for an EU org, new IP, other-org ban, …). Require a second factor before granting sensitive actions (MFA, magic-link re-verification, CAPTCHA, ID verification).
hostile 75 – 100 Hard-block territory. Above 75 the internal enforceLoginRiskScore throws LoginRiskScoreRejectionError; 100 is reached only via a hard-block signal (TOR / active abuse-IP / own-org ban / login flood). Reject the follow-up action, force password reset / step-up auth.

How it is computed: the score is a weighted average of independent signal families (IP reputation, org-country match, EU/high-risk-country policy, per-user history), clamped to [0, 100]. Any single family may return hardBlock(...) to short-circuit the average to 100. Green signals (prior successful login from this IP, paid orders, active session, mobile carrier) subtract from the total; red signals (new IP, other-org ban, foreign continent for an EU org) add to it. Full rule table lives in features/security/riskScore/calculateIpRiskScore.ts.

Stability: the numeric value is a heuristic — the exact algorithm may evolve between releases as new signals are added. The scale, direction and the four bucket boundaries above are stable; integrate against buckets, not exact numbers.

Order-create note: the endpoint currently returns the best-case score (0) for order creation as a placeholder — the order-side risk model is being built out separately (device fingerprint, cart velocity, geo/billing mismatch, chargeback history). The field is exposed already so downstream integrations can wire their step-up logic once and pick up richer values automatically when the order-side model ships.

One of 2:
Variant 1
stringinteger
Default: 0
Variant 2
integer

Server-computed risk score for the current request, on an integer scale 0 – 100 (RISK_SCORE_MIN / RISK_SCORE_MAX in features/security/riskScore/clampScore.ts).

Direction: higher = riskier. 0 is a clean, fully trusted signal; 100 is a hard-block-worthy signal (TOR exit node, active abuse-IP feed hit, login-flood threshold breached, or the caller organisation has explicitly banned the visitor IP).

Recommended integration buckets for the calling application — these are guidance, not a contract, callers are free to pick their own thresholds:

Bucket Range What it means Suggested action
trusted 0 – 19 Green — known device / prior success / same-country / EU IP for an EU org. Proceed silently.
neutral 20 – 49 No strong signal either way (fresh IP, unknown device, no history). Proceed, log the score, optionally show a soft "verify e-mail" nudge.
suspicious 50 – 74 Multiple soft red flags stacked (foreign origin for an EU org, new IP, other-org ban, …). Require a second factor before granting sensitive actions (MFA, magic-link re-verification, CAPTCHA, ID verification).
hostile 75 – 100 Hard-block territory. Above 75 the internal enforceLoginRiskScore throws LoginRiskScoreRejectionError; 100 is reached only via a hard-block signal (TOR / active abuse-IP / own-org ban / login flood). Reject the follow-up action, force password reset / step-up auth.

How it is computed: the score is a weighted average of independent signal families (IP reputation, org-country match, EU/high-risk-country policy, per-user history), clamped to [0, 100]. Any single family may return hardBlock(...) to short-circuit the average to 100. Green signals (prior successful login from this IP, paid orders, active session, mobile carrier) subtract from the total; red signals (new IP, other-org ban, foreign continent for an EU org) add to it. Full rule table lives in features/security/riskScore/calculateIpRiskScore.ts.

Stability: the numeric value is a heuristic — the exact algorithm may evolve between releases as new signals are added. The scale, direction and the four bucket boundaries above are stable; integrate against buckets, not exact numbers.

Order-create note: the endpoint currently returns the best-case score (0) for order creation as a placeholder — the order-side risk model is being built out separately (device fingerprint, cart velocity, geo/billing mismatch, chargeback history). The field is exposed already so downstream integrations can wire their step-up logic once and pick up richer values automatically when the order-side model ships.

Range: 0–100
Examples02560100

Response example

application/json
{
  "success": true,
  "organisationId": 0,
  "organisationSlug": "acme-shop",
  "founderMemberId": 0,
  "founderEmail": "example_founderEmail",
  "riskScore": 0
}

Request example

POST /api/v1/vendor/create-organisation

post
curl -X POST "https://api.bizkithub.com/api/v1/vendor/create-organisation?apiKey=PRODPGrFxpGEtrOZfuWhnoJohUYBXuOE" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
  "organisationName": "Acme Shop",
  "email": "jan@barasek.com",
  "password": "example_password",
  "firstName": "example_firstName",
  "lastName": "example_lastName",
  "locale": "cs",
  "timezone": "Europe/Prague",
  "countryId": 158,
  "description": "example_description",
  "companyRegistrationNumber": "example_companyRegistrationNumber",
  "taxIdentificationNumber": "example_taxIdentificationNumber",
  "checkboxTerms": false,
  "checkboxMarketing": true,
  "customerRealIp": "1.1.1.1"
}'

Need an API key?

All BizKitHub public API endpoints require authentication via API key.

Get API Key