Skip to main content
The aggregate merchant provisioner accepts merchant payloads, applies the partnership-PSP preset for the merchant’s region / cohort, and persists the merchant with PSP configuration already wired. This eliminates the historical 3-step dance (create merchant → write PSP config → flip KYB flag) that operators used to walk by hand.
All admin endpoints below require:
  • JWT with role = SUPER_ADMIN
  • IpAllowlistGuard — caller IP must be in the operator allowlist
  • GeoBlockGuard — caller geo must be permitted
Calls that miss any of the three return 403.

Partnership-PSP preset

Each merchant payload includes a partnership field. The provisioner reads the partnership registry and applies the matching preset:
PartnershipPSPs wiredDefault MoR
shopsadiq-telr-gccTelrShopsadiq
mcredit-bonum-mnBonumMCredit
stripe-direct-usStripeMerchant
paypal-direct-euPayPalMerchant
aggregator-shopsadiqStripe + PayPal + TelrShopsadiq
aggregator-droplinkedStripe + PayPalDroplinked
If partnership is omitted, the provisioner falls back to the region default (documented in the PSP × MoR cohort taxonomy).

POST /admin/aggregate-merchant-provisioner/single

Provisions one merchant.

Authentication

GuardRequirement
JWTRequired, role = SUPER_ADMIN
IP allowlistCaller IP in ADMIN_IP_ALLOWLIST
GeoCountry in ADMIN_GEO_ALLOWLIST

Request body

{
  "email": "founder@unstoppable.example",
  "shopName": "Unstoppable",
  "region": "GCC",
  "partnership": "shopsadiq-telr-gcc",
  "kybCohort": "A-MoR-Shopsadiq-via-Telr",
  "contact": {
    "fullName": "Test Operator",
    "phone": "+9715XXXXXXXX"
  },
  "options": {
    "skipKybVerification": false,
    "seedDemoProducts": true
  }
}
FieldTypeRequiredDescription
emailstringYesMerchant primary email; becomes the JWT sub for the owner
shopNamestringYesStorefront name; subdomain auto-generated from this
regionenum (US | EU | GCC | MN | OTHER)YesDrives PSP defaults and FX rounding
partnershipstringNoPartnership preset key; falls back to region default
kybCohortstringNoOne of the 8 cohort enums; defaults to A-Connect
contact.fullNamestringYesReal name of the merchant contact
contact.phonestringNoE.164 phone
options.skipKybVerificationbooleanNoDefaults false; only set true for internal test merchants
options.seedDemoProductsbooleanNoDefaults false; seeds 3 demo SKUs for walkthrough demos

Response — 201 Created

{
  "merchantId": "65f8a1b2c3d4e5f6a7b8c9aa",
  "shopId": "65f8a1b2c3d4e5f6a7b8c9bb",
  "subdomain": "unstoppable",
  "ownerUserId": "65f8a1b2c3d4e5f6a7b8c9cc",
  "partnership": "shopsadiq-telr-gcc",
  "kybCohort": "A-MoR-Shopsadiq-via-Telr",
  "pspsProvisioned": ["telr"],
  "kybStatus": "PENDING",
  "seededProducts": 3,
  "invitationEmailSent": true
}

Error responses

StatusWhen
400Missing/invalid field; unknown partnership key; unknown kybCohort
403JWT / IP / geo guard failed
409A merchant already exists at this email or subdomain

Example

curl -X POST \
  https://apiv3.droplinked.com/admin/aggregate-merchant-provisioner/single \
  -H "Authorization: Bearer <SUPER_ADMIN_JWT>" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "founder@unstoppable.example",
    "shopName": "Unstoppable",
    "region": "GCC",
    "partnership": "shopsadiq-telr-gcc",
    "kybCohort": "A-MoR-Shopsadiq-via-Telr",
    "contact": { "fullName": "Test Operator" }
  }'

POST /admin/aggregate-merchant-provisioner/bulk

Provisions an array of merchants. Each entry is attempted independently — a failure on one does not abort the batch. The response partitions results into successful and failed.

Request body

{
  "merchants": [
    { "email": "a@example.com", "shopName": "A", "region": "GCC", "partnership": "shopsadiq-telr-gcc", "contact": { "fullName": "A" } },
    { "email": "b@example.com", "shopName": "B", "region": "MN", "partnership": "mcredit-bonum-mn",   "contact": { "fullName": "B" } }
  ]
}
FieldTypeRequiredDescription
merchantsarray (1-100)YesArray of single-provisioner payloads

Response — 207 Multi-Status

{
  "submitted": 2,
  "successful": [
    {
      "index": 0,
      "email": "a@example.com",
      "merchantId": "65f8a1b2c3d4e5f6a7b8c9aa",
      "subdomain": "a"
    }
  ],
  "failed": [
    {
      "index": 1,
      "email": "b@example.com",
      "error": "Subdomain 'b' already taken",
      "code": "SUBDOMAIN_CONFLICT"
    }
  ]
}

Error responses

StatusWhen
400Array empty, > 100 entries, or every entry is structurally invalid
403JWT / IP / geo guard failed
The batch HTTP status is 207 Multi-Status whenever at least one entry succeeds and at least one fails; 201 when all succeed; 400 when all fail validation.

GET /admin/aggregate-merchant-provisioner/preview

Dry-run: validates payloads and resolves partnership presets, but does not write. Useful for “what would happen if I bulk-provisioned this CSV?” before committing. Accepts the same body shape as the bulk endpoint, sent via GET with the payload in the request body (Mintlify renders this — the backend reads JSON from the request body on this route specifically).

Response — 200 OK

{
  "submitted": 2,
  "wouldSucceed": [
    {
      "index": 0,
      "email": "a@example.com",
      "subdomain": "a",
      "pspsThatWouldProvision": ["telr"],
      "kybCohortResolved": "A-MoR-Shopsadiq-via-Telr"
    }
  ],
  "wouldFail": [
    {
      "index": 1,
      "email": "b@example.com",
      "error": "Subdomain 'b' already taken",
      "code": "SUBDOMAIN_CONFLICT"
    }
  ],
  "warnings": [
    "Merchant 'a@example.com' has no `kybCohort` — would default to `A-Connect` based on region GCC"
  ]
}

Example

curl -X GET \
  https://apiv3.droplinked.com/admin/aggregate-merchant-provisioner/preview \
  -H "Authorization: Bearer <SUPER_ADMIN_JWT>" \
  -H "Content-Type: application/json" \
  -d '{
    "merchants": [
      { "email": "a@example.com", "shopName": "A", "region": "GCC", "contact": { "fullName": "A" } }
    ]
  }'

Operational notes

  • Idempotency: the provisioner is not idempotent. Re-submitting the same email returns 409. Use the preview endpoint to scrub a CSV before running bulk.
  • Email invitations: the owner-user invitation email is sent synchronously on successful provision. If your operator workflow sends a custom welcome email, set options.skipInvitationEmail (boolean, default false).
  • Cohort routing: the kybCohort field flows downstream into the lending eligibility, cost-comparator, and affiliate-commission projections. Choose intentionally.
  • Bonum admin — per-merchant Bonum config (set after provisioning when partnership = mcredit-bonum-mn).
  • Telr admin — Telr reconcile for partnerships that wire Telr.
  • Network Health KPIs — track verified-shops + GMV growth after bulk provisioning.