Investors

Bulk create investors (deprecated)

POST/investors/bulk

Creates many investors in one call — the import path for migrations. Each row has the same shape as POST /investors (email, title, first_name, last_name + optional sso_id, external_id, onboarding_uuid and a data_capture map keyed by UDC field reference), with email at most 53 characters. Unlike POST /investors/bulk/create, it has no create-or-return match: a row whose email already belongs to an investor fails with "The email has already been taken." Use POST /investors/bulk/create for create-or-return. A token whose organisation is not the platform organisation gets 422 validation_failed with detail rule platform_organisation, and no row is processed.

Send behaviour mirrors the platform's direct-create: if the organisation has a ClientInvite approval workflow the invite is held; otherwise the invite is sent. Set the request-level auto_send to No to import silently regardless (the common choice for a CRM migration), or Yes to force sending.

Each row needs an onboarding_uuid whenever the organisation has published at least one onboarding journey, exactly as POST /investors does; rows without one are reported as failed rather than imported journeyless.

The batch is best-effort: a row that fails (duplicate email, unknown UDC reference, missing onboarding journey, …) is reported in results with its error and does not abort the rest. Maximum 500 rows per call.

Deprecated: withdrawn on 2027-06-30. POST /investors/bulk/create replaces it, with items, per-item auto_send and a job above 100 items; every answer carries a Link header with rel="successor-version". This route keeps its old body and answer.

Request body

application/json, required

  • auto_sendstring#

    Overrides the approval-aware default for every row. Omit to use the default (send unless a ClientInvite approval workflow exists); No imports silently.

    One of: Yes, No
  • investorsobject[]Required#
{
  "auto_send": "No",
  "investors": [
    {
      "email": "stuart@example.com",
      "title": "Mr",
      "first_name": "Stuart",
      "last_name": "Parkinson",
      "external_id": "CRM-ODS-row3",
      "data_capture": {
        "phone_number": "+44 20 7946 0000"
      }
    },
    {
      "email": "adam@example.com",
      "title": "Dr",
      "first_name": "Adam",
      "last_name": "Reynolds"
    }
  ]
}

Responses

200

Per-row result report. created/failed/invites_sent summarise the batch; results lists each row's outcome in request order.

{
  "created": 1,
  "failed": 1,
  "invites_sent": 0,
  "results": [
    {
      "index": 0,
      "email": "stuart@example.com",
      "status": "created",
      "id": "9a1f0028-1111-2222-3333-444455556666",
      "invite_sent": false
    },
    {
      "index": 1,
      "email": "adam@example.com",
      "status": "failed",
      "error": "The email has already been taken."
    }
  ]
}
401

Authentication failed. error.code is one of invalid_token, expired, revoked.

Error
403

Authorization failed. error.code is one of org_disabled, ip_blocked, missing_scope, scope_disabled, user_disabled.

Error
422

Request validation failed (HTTP 422). The body is either the Laravel validation envelope (message + field-keyed errors) or the API error envelope for domain rejections (e.g. form_type_not_supported, folder_locked, parent_not_found, folder_not_empty).

429

Rate limit exceeded (error.code = rate_limited).

Error

Examples

curl -X POST https://your-org-domain/kong/core/api/v2/investors/bulk\
  -H "Authorization: Bearer $HELM_API_KEY"\
  -H "Content-Type: application/json"\
  -d '{
  "auto_send": "No",
  "investors": [
    {
      "email": "stuart@example.com",
      "title": "Mr",
      "first_name": "Stuart",
      "last_name": "Parkinson",
      "external_id": "CRM-ODS-row3",
      "data_capture": {
        "phone_number": "+44 20 7946 0000"
      }
    },
    {
      "email": "adam@example.com",
      "title": "Dr",
      "first_name": "Adam",
      "last_name": "Reynolds"
    }
  ]
}'