curl -X POST https://app.instantcompliance.ai/api/v1/entities \
-H "Authorization: Bearer ic_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"externalId": "crm-ent-311",
"type": "COMPANY",
"name": "Acme Holdings Pty Ltd",
"abn": "12345678901",
"contact": { "fullName": "Jane Doe", "email": "jane@example.com" }
}'await fetch('https://app.instantcompliance.ai/api/v1/entities', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.IC_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({
externalId: 'crm-ent-311',
type: 'COMPANY',
name: 'Acme Holdings Pty Ltd',
abn: '12345678901',
contact: { fullName: 'Jane Doe', email: 'jane@example.com' }
})
});
import os, uuid, requests
requests.post(
'https://app.instantcompliance.ai/api/v1/entities',
headers={
'Authorization': f"Bearer {os.environ['IC_API_KEY']}",
'Idempotency-Key': str(uuid.uuid4())
},
json={
'externalId': 'crm-ent-311',
'type': 'COMPANY',
'name': 'Acme Holdings Pty Ltd',
'abn': '12345678901',
'contact': {'fullName': 'Jane Doe', 'email': 'jane@example.com'}
}
)
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.instantcompliance.ai/api/v1/entities",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'externalId' => 'crm-ent-311',
'type' => 'COMPANY',
'name' => 'Acme Holdings Pty Ltd'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.instantcompliance.ai/api/v1/entities"
payload := strings.NewReader("{\n \"externalId\": \"crm-ent-311\",\n \"type\": \"COMPANY\",\n \"name\": \"Acme Holdings Pty Ltd\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.instantcompliance.ai/api/v1/entities")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"externalId\": \"crm-ent-311\",\n \"type\": \"COMPANY\",\n \"name\": \"Acme Holdings Pty Ltd\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.instantcompliance.ai/api/v1/entities")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"externalId\": \"crm-ent-311\",\n \"type\": \"COMPANY\",\n \"name\": \"Acme Holdings Pty Ltd\"\n}"
response = http.request(request)
puts response.read_body{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"external_id": "crm-ent-311",
"type": "COMPANY",
"name": "Acme Holdings Pty Ltd",
"origin": "AUSTRALIAN",
"abn": "12345678901",
"acn": "123456789",
"country_of_formation": "AUS",
"registration_number": null,
"kyb_status": "IN_PROGRESS",
"kyb_started_at": "2026-06-23T01:00:00Z",
"kyb_completed_at": null,
"contact": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"external_id": "crm-7741",
"full_name": "Jane Doe",
"email": "jane@example.com",
"kyc_status": "PENDING",
"kyc_required": true
},
"aml": {
"status": "CLEAR",
"screened_at": "2026-06-23T01:12:00Z",
"last_reviewed_at": null,
"flags": {
"pep": false,
"sanctions": false,
"adverse_media": false,
"terrorism": false
}
},
"added_via": "INTEGRATION",
"created_at": "2026-06-22T22:14:00Z",
"updated_at": "2026-06-23T01:12:05Z"
}{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"external_id": "crm-ent-311",
"type": "COMPANY",
"name": "Acme Holdings Pty Ltd",
"origin": "AUSTRALIAN",
"abn": "12345678901",
"acn": "123456789",
"country_of_formation": "AUS",
"registration_number": null,
"kyb_status": "IN_PROGRESS",
"kyb_started_at": "2026-06-23T01:00:00Z",
"kyb_completed_at": null,
"contact": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"external_id": "crm-7741",
"full_name": "Jane Doe",
"email": "jane@example.com",
"kyc_status": "PENDING",
"kyc_required": true
},
"aml": {
"status": "CLEAR",
"screened_at": "2026-06-23T01:12:00Z",
"last_reviewed_at": null,
"flags": {
"pep": false,
"sanctions": false,
"adverse_media": false,
"terrorism": false
}
},
"added_via": "INTEGRATION",
"created_at": "2026-06-22T22:14:00Z",
"updated_at": "2026-06-23T01:12:05Z"
}{
"error": {
"code": "unauthorized",
"message": "Invalid or revoked API key."
}
}{
"error": {
"code": "forbidden_scope",
"message": "This API key does not have the required scope. customers:write",
"details": {
"required_scope": "customers:write"
}
}
}{
"error": {
"code": "unauthorized",
"message": "<string>",
"details": {}
}
}{
"error": {
"code": "validation_failed",
"message": "Invalid customer payload.",
"details": {
"issues": {
"email": [
"Must be a valid email address."
]
}
}
}
}{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded. Slow down and retry shortly.",
"details": {
"limit": 60,
"window_seconds": 60
}
}
}Create or upsert an entity
Ingest an entity customer (company, trust, partnership, SMSF). The
record is created with added_via = INTEGRATION, and kyb_status
is derived by the same AML/CTF rule the in-app onboarding uses:
providing a designated service makes customer due diligence required
(NOT_STARTED — KYB is owed but not begun), otherwise the record
lands NOT_REQUIRED. No KYB is triggered and no credits are
charged — your back-office team starts verification (and chooses
the KYB method) from inside Instant Compliance.
Identifiers
AUSTRALIANentities (the default) acceptabn(11 digits) andacn(9 digits). Spaces are tolerated and stripped.INTERNATIONALentities requirecountryOfFormation(ISO 3166-1 alpha-3) and acceptregistrationNumber;abn/acnare rejected.
Contact person
KYB needs an individual who acts for the entity. Pass contact
with either customerId (an existing individual’s UUID or
external_id) or an inline fullName + email (which reuses an
exact name+email match or creates a contact-role individual).
contact.kycRequired (default true) records the intent to
verify the contact alongside the entity’s KYB — nothing is sent,
armed, or billed until your team starts the KYB in-app.
Designated services
designatedServices behaves exactly as on POST /customers: the
list replaces the entity’s set, derives the designated flag,
recomputes risk, and an explicit empty array (or
risk.isDesignatedService: false without a list) creates the
record with kyb_status = NOT_REQUIRED.
Existing (pre-commencement) clients
With risk.isNewCustomer: false you can supply clientSinceDate,
the s36(4) preCommencementAssessment, and preExistingServices —
same rules and outcomes as on POST /customers (a no-trigger
assessment keeps the entity monitoring-only).
Idempotency
- If
externalIdis supplied and already exists in your organisation, the existing record is updated and the response is 200 OK. - Otherwise a match on
abn, thenacn, then legal name + entity type (case-insensitive) updates that record — unless it carries a differentexternal_id, which is a 409 conflict. - Otherwise a new record is created and returned with 201 Created.
type and origin are immutable — an upsert that disagrees with
the stored values is a 409 conflict. Once the entity’s KYB is
VERIFIED, identity fields (name, abn, acn,
countryOfFormation) freeze — re-sending unchanged values is fine,
changing them returns 422 validation_failed.
Include an Idempotency-Key header to make network retries safe
for 24 hours.
curl -X POST https://app.instantcompliance.ai/api/v1/entities \
-H "Authorization: Bearer ic_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"externalId": "crm-ent-311",
"type": "COMPANY",
"name": "Acme Holdings Pty Ltd",
"abn": "12345678901",
"contact": { "fullName": "Jane Doe", "email": "jane@example.com" }
}'await fetch('https://app.instantcompliance.ai/api/v1/entities', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.IC_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({
externalId: 'crm-ent-311',
type: 'COMPANY',
name: 'Acme Holdings Pty Ltd',
abn: '12345678901',
contact: { fullName: 'Jane Doe', email: 'jane@example.com' }
})
});
import os, uuid, requests
requests.post(
'https://app.instantcompliance.ai/api/v1/entities',
headers={
'Authorization': f"Bearer {os.environ['IC_API_KEY']}",
'Idempotency-Key': str(uuid.uuid4())
},
json={
'externalId': 'crm-ent-311',
'type': 'COMPANY',
'name': 'Acme Holdings Pty Ltd',
'abn': '12345678901',
'contact': {'fullName': 'Jane Doe', 'email': 'jane@example.com'}
}
)
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.instantcompliance.ai/api/v1/entities",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'externalId' => 'crm-ent-311',
'type' => 'COMPANY',
'name' => 'Acme Holdings Pty Ltd'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.instantcompliance.ai/api/v1/entities"
payload := strings.NewReader("{\n \"externalId\": \"crm-ent-311\",\n \"type\": \"COMPANY\",\n \"name\": \"Acme Holdings Pty Ltd\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.instantcompliance.ai/api/v1/entities")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"externalId\": \"crm-ent-311\",\n \"type\": \"COMPANY\",\n \"name\": \"Acme Holdings Pty Ltd\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.instantcompliance.ai/api/v1/entities")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"externalId\": \"crm-ent-311\",\n \"type\": \"COMPANY\",\n \"name\": \"Acme Holdings Pty Ltd\"\n}"
response = http.request(request)
puts response.read_body{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"external_id": "crm-ent-311",
"type": "COMPANY",
"name": "Acme Holdings Pty Ltd",
"origin": "AUSTRALIAN",
"abn": "12345678901",
"acn": "123456789",
"country_of_formation": "AUS",
"registration_number": null,
"kyb_status": "IN_PROGRESS",
"kyb_started_at": "2026-06-23T01:00:00Z",
"kyb_completed_at": null,
"contact": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"external_id": "crm-7741",
"full_name": "Jane Doe",
"email": "jane@example.com",
"kyc_status": "PENDING",
"kyc_required": true
},
"aml": {
"status": "CLEAR",
"screened_at": "2026-06-23T01:12:00Z",
"last_reviewed_at": null,
"flags": {
"pep": false,
"sanctions": false,
"adverse_media": false,
"terrorism": false
}
},
"added_via": "INTEGRATION",
"created_at": "2026-06-22T22:14:00Z",
"updated_at": "2026-06-23T01:12:05Z"
}{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"external_id": "crm-ent-311",
"type": "COMPANY",
"name": "Acme Holdings Pty Ltd",
"origin": "AUSTRALIAN",
"abn": "12345678901",
"acn": "123456789",
"country_of_formation": "AUS",
"registration_number": null,
"kyb_status": "IN_PROGRESS",
"kyb_started_at": "2026-06-23T01:00:00Z",
"kyb_completed_at": null,
"contact": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"external_id": "crm-7741",
"full_name": "Jane Doe",
"email": "jane@example.com",
"kyc_status": "PENDING",
"kyc_required": true
},
"aml": {
"status": "CLEAR",
"screened_at": "2026-06-23T01:12:00Z",
"last_reviewed_at": null,
"flags": {
"pep": false,
"sanctions": false,
"adverse_media": false,
"terrorism": false
}
},
"added_via": "INTEGRATION",
"created_at": "2026-06-22T22:14:00Z",
"updated_at": "2026-06-23T01:12:05Z"
}{
"error": {
"code": "unauthorized",
"message": "Invalid or revoked API key."
}
}{
"error": {
"code": "forbidden_scope",
"message": "This API key does not have the required scope. customers:write",
"details": {
"required_scope": "customers:write"
}
}
}{
"error": {
"code": "unauthorized",
"message": "<string>",
"details": {}
}
}{
"error": {
"code": "validation_failed",
"message": "Invalid customer payload.",
"details": {
"issues": {
"email": [
"Must be a valid email address."
]
}
}
}
}{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded. Slow down and retry shortly.",
"details": {
"limit": 60,
"window_seconds": 60
}
}
}INTEGRATION, and
kyb_status is derived by the same AML/CTF rule as in-app onboarding:
providing a designated service makes due diligence required
(NOT_STARTED — KYB is owed but not begun); an explicit “no designated
services” (empty designatedServices list, or
risk.isDesignatedService: false) lands NOT_REQUIRED. No KYB is
triggered. No credits are charged. Your back-office team chooses the
KYB method and starts verification in-app when ready.
The follow-on lifecycle steps are all reachable over the API too:
Start KYB, then — where the lane needs them —
add beneficial owners and
send their verification links, or
request documents /
upload the company document.
Those steps do email and bill, unlike this one.
Individuals vs entities
Entities verify via KYB (business verification + beneficial-owner resolution) instead of KYC, so they live on their own resource. UsePOST /customers for individuals
and sole traders.
Identifiers
AUSTRALIANentities (the default) takeabn(11 digits) andacn(9 digits). Spaces are tolerated and stripped.INTERNATIONALentities requirecountryOfFormation(ISO 3166-1 alpha-3) and acceptregistrationNumber;abn/acnare rejected.
Contact person
KYB needs an individual who acts for the entity — they receive the verification link when your team starts KYB. Passcontact with either
customerId (an existing individual’s UUID or external_id) or an
inline fullName + email. Inline contacts reuse an exact name+email
match or create a contact-role individual; nothing is emailed or billed
at ingest time.
Existing (pre-commencement) clients
Withrisk.isNewCustomer: false you can also supply clientSinceDate,
the s36(4) preCommencementAssessment, and preExistingServices — the
same fields and rules as on
POST /customers: a recorded
no-trigger assessment keeps the entity on the monitoring-only carve-out
(kyb_status = NOT_REQUIRED with services recorded); the assessment is
create-time only.
Idempotent upsert behaviour
| Input | Match found? | Result | Status |
|---|---|---|---|
externalId supplied | Yes (same org) | Update existing record | 200 OK |
| — | abn, then acn, then legal name + type matches (no conflicting externalId) | Adopt + update | 200 OK |
| — | Match carries a different externalId | Reject | 409 conflict |
| — | Stored type/origin disagree with the payload | Reject | 409 conflict |
| Nothing matches | — | Create new | 201 Created |
name, abn,
acn, countryOfFormation) freeze — re-sending unchanged values is
fine, changing them returns 422 validation_failed.
Retry safety
PassIdempotency-Key (a UUID or any string ≤ 255 chars). Repeated
calls with the same key within 24 hours replay the original response.
See Idempotency.Authorizations
Bearer API key issued from Settings → Developers in your
Instant Compliance organisation. Format: ic_live_….
Headers
Caller-supplied unique key for safe retries. Repeat the same key within 24 hours and we replay the original response instead of repeating the side effect. Reusing the key with a different request body returns 409 idempotency_conflict.
255Body
Entity customer types accepted by /entities. SMSF is treated
as a type of trust and OTHER as a company for verification
purposes, but the type you post round-trips back unchanged.
Immutable after creation.
COMPANY, TRUST, PARTNERSHIP, SMSF, OTHER The entity's legal name.
1 - 255Your CRM identifier. Strongly recommended for idempotent upsert + round-tripping.
255Where the entity is formed. AUSTRALIAN (the default) uses
ABN/ACN identifiers; INTERNATIONAL uses countryOfFormation +
registrationNumber. Immutable after creation.
AUSTRALIAN, INTERNATIONAL Australian Business Number (11 digits; spaces tolerated). AUSTRALIAN entities only.
Australian Company Number (9 digits; spaces tolerated). AUSTRALIAN entities only.
500ISO 3166-1 alpha-3. Required for INTERNATIONAL entities; rejected for AUSTRALIAN.
Company/registry number in the country of formation. INTERNATIONAL entities only.
64The entity's primary contact person — the individual who acts for
the entity and receives the KYB portal link when your team starts
verification. Provide either customerId (an existing
individual) or fullName + email (inline create/reuse) —
not both.
Show child attributes
Show child attributes
When the relationship began (yyyy-mm-dd, not in the future). Existing clients only — requires risk.isNewCustomer: false.
The s36(4) trigger assessment for an existing (pre-commencement)
client receiving new designated services. Requires
risk.isNewCustomer: false and a non-empty designatedServices
list. Initial CDD is triggered only when BOTH limbs are met — a
significant change in the relationship AND resulting medium/high
risk. A recorded no-trigger assessment keeps the client on the
monitoring-only carve-out (kyc_status/kyb_status = NOT_REQUIRED) even though services are recorded; without an
assessment the conservative rule applies (any designated service ⇒
CDD required).
Show child attributes
Show child attributes
Designated services you were ALREADY providing before commencement. History + risk only — never triggers CDD. Requires risk.isNewCustomer: false; cannot be combined with a non-empty designatedServices list.
6464["PRO-FORMATION", "PRO-CLIENT-MONEY"]
Approximate start date of the pre-existing service(s).
Optional risk pre-answers. Your back-office team will complete the full risk assessment in-app before triggering KYC; these values seed it.
Show child attributes
Show child attributes
The specific designated service(s) provided to the customer, as
stable catalog codes. An empty array is an explicit "no designated
services". Unknown or non-designated codes are rejected with
422 validation_failed.
The catalog is also available programmatically via
GET /designated-services.
Professional services
| Code | Service |
|---|---|
PRO-FORMATION | Company or trust formation |
PRO-PURCHASE-SALE | Company or trust purchase/sale |
PRO-RESTRUCTURE | Business restructuring services |
PRO-REAL-ESTATE | Real estate professional services (conveyancing / legal) |
PRO-CLIENT-MONEY | Client money/assets management (incl. securities & virtual assets) |
PRO-FINANCING | Business financing arrangement services |
PRO-SHELF | Shelf company sales |
PRO-NOMINEE | Nominee director or shareholder services |
PRO-REGISTERED-OFFICE | Registered office address services |
Real estate
| Code | Service |
|---|---|
RE-BROKER | Real estate agency (brokering sales) |
RE-DEVELOPER | Property development sales |
Financial services
| Code | Service |
|---|---|
FIN-BANKING | Banking and deposit services |
FIN-REMITTANCE | Money transfer and remittance services |
FIN-CRYPTO | Cryptocurrency exchange services |
FIN-FX | Foreign currency exchange |
FIN-LOANS | Loans and financing services |
FIN-INVESTMENT | Investment and securities services |
FIN-ADVISORY | Financial advisory services (arranging designated services) |
FIN-INSURANCE | Insurance services (life insurance, sinking funds) |
FIN-SUPER | Superannuation fund management |
FIN-PENSION | Pension, annuity, or retirement account services |
FIN-STORED-VALUE | High-value stored value cards |
FIN-CUSTODIAL | Custodial and depository services |
FIN-PAYROLL | Payroll services for other businesses |
Other designated services
| Code | Service |
|---|---|
OTH-BULLION | Bullion trading (precious metals) |
OTH-GAMBLING | Gambling services |
OTH-HIGH-VALUE-GOODS | High-value goods dealing |
6464["PRO-FORMATION", "PRO-CLIENT-MONEY"]
Response
Existing entity updated (idempotent upsert).
Instant Compliance customer UUID (entities are customers too).
Entity customer types accepted by /entities. SMSF is treated
as a type of trust and OTHER as a company for verification
purposes, but the type you post round-trips back unchanged.
Immutable after creation.
COMPANY, TRUST, PARTNERSHIP, SMSF, OTHER The entity's legal name.
Where the entity is formed. AUSTRALIAN (the default) uses
ABN/ACN identifiers; INTERNATIONAL uses countryOfFormation +
registrationNumber. Immutable after creation.
AUSTRALIAN, INTERNATIONAL NOT_STARTED, PENDING, IN_PROGRESS, VERIFIED, FAILED, NOT_REQUIRED, AWAITING_RESUBMISSION Show child attributes
Show child attributes
How the record entered Instant Compliance.
ADMIN_MANUAL, AI_EXTRACTED, CONTACT_PORTAL, BULK_IMPORT, INTEGRATION, SYSTEM ISO 3166-1 alpha-3. AUS for Australian entities; the country
of formation for international entities.
Registry number recorded for international entities.
The entity's current contact person, or null when none is set.
Show child attributes
Show child attributes

