RecipesCreate an account with contacts
Create an account with contacts
Create an account, add its contacts in one batch with per-item results, and read them back, in REST or GraphQL, in cURL, TypeScript or Python.
Answer
Look up the owner by e-mail, create the account with name and owner_id, then add the contacts in one batch with account_relations, one primary relation each. Send rollback-method=1 (GraphQL ROLLBACK_FAILED) so one bad contact does not undo the others, check every item, and read the contacts back by account.
Create Northwind Analytics with two contacts linked to it, owned by the same sales rep. You end with one account, two contacts and their account relations, read back from the API.
Before you start
| Permissions | Full Access to Accounts and Contacts on the Records tab of the application's permissions. The owner lookup also reads users (Clients). |
|---|---|
validation-level |
The default (0, full validation). Do not relax it for new records; see Validation. |
| Batch behaviour | Step 3 is a batch sent with rollback-method=1 (GraphQL rollbackMethod: ROLLBACK_): it answers 200 even if a contact fails. The valid contacts are written, and a failed one comes back as an error object. The program checks every item. |
| Context | Region and space ID come from the context bar. Credentials come from $COEVERA_ and $COEVERA_. |
The programs need curl and jq, Node.js 18 or later, or Python 3.9 or later with requests.
Authentication shows where the credentials come from, and
Permissions and scopes where to set the permissions.
The operations this recipe calls, in the API reference: REST
GET /entities/Clients,
POST /entities/Accounts,
POST /entities/Contacts/batch-modify,
GET /entities/Contacts; GraphQL
ClientRepository.getByCriteria,
createAccount,
bulkUpdateContact,
ContactRepository.getByCriteria.
Set up a helper
Every step below uses this setup, so the steps show only what changes. Export the credentials first:
export COEVERA_API_KEY="<application user name>"
export COEVERA_API_SECRET="<application password>" # never commit itCOEVERA_BASE="https://{region}.api.coevera.com/api/v100/rest/spaces/{space_id}"const base = `https://{region}.api.coevera.com/api/v100/rest/spaces/{space_id}`;
const auth = Buffer.from(`${env.COEVERA_API_KEY}:${env.COEVERA_API_SECRET}`).toString("base64");
async function coevera(path: string, init: { method?: string; body?: unknown } = {}) {
const res = await fetch(`${base}${path}`, {
method: init.method ?? "GET",
headers: { Authorization: `Basic ${auth}`, "Content-Type": "application/json" },
body: init.body === undefined ? undefined : JSON.stringify(init.body),
});
const body = await res.json();
if (!res.ok) throw new Error(`HTTP ${res.status} ${body.name}: ${body.message}`);
return body;
}BASE = "https://{region}.api.coevera.com/api/v100/rest/spaces/{space_id}"
session = requests.Session()
session.auth = (os.environ["COEVERA_API_KEY"], os.environ["COEVERA_API_SECRET"])
def coevera(method, path, **kwargs):
response = session.request(method, f"{BASE}{path}", timeout=30, **kwargs)
body = response.json()
if not response.ok:
sys.exit(f"HTTP {response.status_code} {body.get('name')}: {body.get('message')}")
return bodyCOEVERA_GRAPHQL="https://{region}.api.coevera.com/api/v100/app/space/{space_id}/graphql/public"
# gql: POST the GraphQL request read from stdin. GraphQL reports errors in the body, often
# with HTTP 200, so fail on those too.
gql() {
local response
response=$(curl -sS --fail-with-body "$COEVERA_GRAPHQL" \
-u "$COEVERA_API_KEY:$COEVERA_API_SECRET" \
-H "Content-Type: application/json" \
-d @-)
if jq -e '.errors' <<<"$response" >/dev/null; then
echo "$response" >&2
return 1
fi
echo "$response"
}const endpoint = `https://{region}.api.coevera.com/api/v100/app/space/{space_id}/graphql/public`;
const auth = Buffer.from(`${env.COEVERA_API_KEY}:${env.COEVERA_API_SECRET}`).toString("base64");
// gql: one GraphQL request. GraphQL reports errors in the body, often with HTTP 200.
async function gql(query: string, variables: object = {}) {
const res = await fetch(endpoint, {
method: "POST",
headers: { Authorization: `Basic ${auth}`, "Content-Type": "application/json" },
body: JSON.stringify({ query, variables }),
});
const body = await res.json();
if (!res.ok || body.errors) throw new Error(`HTTP ${res.status}: ${JSON.stringify(body.errors ?? body)}`);
return body;
}ENDPOINT = (
"https://{region}.api.coevera.com/api/v100/app/space/"
"{space_id}/graphql/public"
)
session = requests.Session()
session.auth = (os.environ["COEVERA_API_KEY"], os.environ["COEVERA_API_SECRET"])
def gql(query, variables=None):
"""One GraphQL request. GraphQL reports errors in the body, often with HTTP 200."""
response = session.post(ENDPOINT, json={"query": query, "variables": variables or {}}, timeout=30)
body = response.json()
if not response.ok or body.get("errors"):
sys.exit(f"HTTP {response.status_code}: {json.dumps(body.get('errors', body))}")
return body["data"]1. Find the owner
Every account and contact needs an owner. Look the sales rep up by e-mail in Clients, the API
name for users. The programs encode the address: a raw + in a query string arrives as a space.
owner=$(curl -sS --fail-with-body -G "$COEVERA_BASE/entities/Clients" \
--data-urlencode "filter[email]=dana.whitfield@example.com" \
-d load-only=id,name,default_unit_id \
-u "$COEVERA_API_KEY:$COEVERA_API_SECRET")
echo "$owner"
OWNER_ID=$(jq -er '.data[0].id' <<<"$owner")// URLSearchParams encodes the address: a raw "+" in a query string arrives as a space.
const lookup = new URLSearchParams({
"filter[email]": "dana.whitfield@example.com",
"load-only": "id,name,default_unit_id",
});
const owners = await coevera(`/entities/Clients?${lookup}`);
const ownerId: string | undefined = owners.data[0]?.id;
if (!ownerId) throw new Error("No user with that e-mail in this space.");
console.log("Owner:", owners.data[0].name, ownerId);owners = coevera(
"GET",
"/entities/Clients",
params={"filter[email]": "dana.whitfield@example.com", "load-only": "id,name,default_unit_id"},
)
if not owners["data"]:
sys.exit("No user with that e-mail in this space.")
owner_id = owners["data"][0]["id"]
print("Owner:", owners["data"][0]["name"], owner_id)owner=$(jq -n '{
query: "{ entities { client { getByCriteria(first: 1, filter: { email: { eq: [\"dana.whitfield@example.com\"] } }) { edges { node { id name } } } } } }"
}' | gql)
echo "$owner"
OWNER_ID=$(jq -er '.data.entities.client.getByCriteria.edges[0].node.id' <<<"$owner")const { data: found } = await gql(`{
entities {
client {
getByCriteria(first: 1, filter: { email: { eq: ["dana.whitfield@example.com"] } }) {
edges { node { id name } }
}
}
}
}`);
const ownerId: string | undefined = found.entities.client.getByCriteria.edges[0]?.node.id;
if (!ownerId) throw new Error("No user with that e-mail in this space.");
console.log("Owner:", found.entities.client.getByCriteria.edges[0].node.name, ownerId);found = gql("""
{
entities {
client {
getByCriteria(first: 1, filter: { email: { eq: ["dana.whitfield@example.com"] } }) {
edges { node { id name } }
}
}
}
}
""")
edges = found["entities"]["client"]["getByCriteria"]["edges"]
if not edges:
sys.exit("No user with that e-mail in this space.")
owner_id = edges[0]["node"]["id"]
print("Owner:", edges[0]["node"]["name"], owner_id){
"success": true,
"total": 1,
"page_info": {
"start_cursor": "WyIwZDAwMDAwMC0wMDAwLTQwMDAtYTAwMC0wMDAwMDAwMDAwMDEiXQ==",
"end_cursor": "WyIwZDAwMDAwMC0wMDAwLTQwMDAtYTAwMC0wMDAwMDAwMDAwMDEiXQ==",
"has_previous_page": false,
"has_next_page": false
},
"data": [
{
"id": "0d000000-0000-4000-a000-000000000001",
"default_unit_id": "05000000-0000-4000-a000-000000000001",
"name": "Dana Whitfield"
}
]
}{
"data": {
"entities": {
"client": {
"getByCriteria": {
"edges": [
{
"node": {
"id": "0d000000-0000-4000-a000-000000000001",
"name": "Dana Whitfield"
}
}
]
}
}
}
}
}2. Create the account
name and owner_id are required: without either, the API answers 422. The sales unit
(unit_id) defaults to the owner's default unit.
account=$(curl -sS --fail-with-body "$COEVERA_BASE/entities/Accounts" \
-u "$COEVERA_API_KEY:$COEVERA_API_SECRET" \
-H "Content-Type: application/json" \
-d @- <<JSON
{
"name": "Northwind Analytics",
"owner_id": "$OWNER_ID",
"email1": "northwind@example.com",
"phone1": "+1 206 555 0100",
"home_page": "https://northwind.example.com",
"city": "Seattle",
"country": "United States"
}
JSON
)
echo "$account"
ACCOUNT_ID=$(jq -er '.data.id' <<<"$account")const account = await coevera("/entities/Accounts", {
method: "POST",
body: {
name: "Northwind Analytics",
owner_id: ownerId,
email1: "northwind@example.com",
phone1: "+1 206 555 0100",
home_page: "https://northwind.example.com",
city: "Seattle",
country: "United States",
},
});
const accountId: string = account.data.id;
console.log("Account:", accountId);account = coevera(
"POST",
"/entities/Accounts",
json={
"name": "Northwind Analytics",
"owner_id": owner_id,
"email1": "northwind@example.com",
"phone1": "+1 206 555 0100",
"home_page": "https://northwind.example.com",
"city": "Seattle",
"country": "United States",
},
)
account_id = account["data"]["id"]
print("Account:", account_id)account=$(jq -n --arg owner "$OWNER_ID" '{
query: "mutation CreateAccount($input: CreateAccountInput!) { createAccount(input: $input) { account { id name owner { id name } } } }",
variables: {
input: {
name: "Northwind Analytics",
ownerId: $owner,
email1: "northwind@example.com",
phone1: "+1 206 555 0100",
homePage: "https://northwind.example.com",
city: "Seattle",
country: "United States"
}
}
}' | gql)
echo "$account"
ACCOUNT_ID=$(jq -er '.data.createAccount.account.id' <<<"$account")const { data: created } = await gql(
`mutation CreateAccount($input: CreateAccountInput!) {
createAccount(input: $input) {
account { id name owner { id name } }
}
}`,
{
input: {
name: "Northwind Analytics",
ownerId,
email1: "northwind@example.com",
phone1: "+1 206 555 0100",
homePage: "https://northwind.example.com",
city: "Seattle",
country: "United States",
},
},
);
const accountId: string = created.createAccount.account.id;
console.log("Account:", accountId);created = gql(
"""
mutation CreateAccount($input: CreateAccountInput!) {
createAccount(input: $input) {
account { id name owner { id name } }
}
}
""",
{
"input": {
"name": "Northwind Analytics",
"ownerId": owner_id,
"email1": "northwind@example.com",
"phone1": "+1 206 555 0100",
"homePage": "https://northwind.example.com",
"city": "Seattle",
"country": "United States",
}
},
)
account_id = created["createAccount"]["account"]["id"]
print("Account:", account_id){
"success": true,
"data": {
"is_delete_protected": false,
"id": "0a000000-0000-4000-a000-000000000001",
"is_deleted": false,
"modified": "2026-10-15 09:00:00.000000+00:00",
"created": "2026-10-15 09:00:00.000040+00:00",
"account_type": "https://eu-central.api.coevera.com/api/v100/rest/spaces/nv2_northwind_docs/entities/AccountTypes/03000000-0000-4000-a000-000000000001",
"currency_exchange_rates_list": null,
"customer_type": null,
"industry": null,
"owner": "https://eu-central.api.coevera.com/api/v100/rest/spaces/nv2_northwind_docs/entities/Clients/0d000000-0000-4000-a000-000000000001",
"parent_account": null,
"parent_account_relation_type": null,
"picture": null,
"unit": "https://eu-central.api.coevera.com/api/v100/rest/spaces/nv2_northwind_docs/entities/SalesUnits/05000000-0000-4000-a000-000000000001",
"account_class": 1,
"account_type_id": "03000000-0000-4000-a000-000000000001",
"address": "",
"city": "Seattle",
"comments": "",
"country": "United States",
"currency_exchange_rates_list_id": null,
"customer_type_id": null,
"email1": "northwind@example.com",
"email2": "",
"email3": "",
"email4": "",
"email5": "",
"health_category": null,
"health_status": null,
"home_page": "https://northwind.example.com",
"industry_id": null,
"is_archived": false,
"last_ai_account_hierarchy_applied": null,
"last_ai_org_chart_applied": null,
"name": "Northwind Analytics",
"owner_id": "0d000000-0000-4000-a000-000000000001",
"parent_account_id": null,
"parent_account_relation_type_id": null,
"phone1": "+1 206 555 0100",
"phone2": "",
"phone3": "",
"phone4": "",
"phone5": "",
"picture_id": null,
"quick_parent_account_name": "",
"share_mode": 0,
"state_province": "",
"unit_id": "05000000-0000-4000-a000-000000000001",
"zip_code": "",
"revision": 411,
"formatted_name": "Northwind Analytics",
"social_media": null,
"sharing_units": [],
"sharing_clients": [],
"documents": [],
"is_favorite": false,
"is_unsubscribed": false,
"tags": [],
"static_profiles": [],
"health": null,
"modified_by_user": null
}
}Show all 66 linesShow less
{
"data": {
"createAccount": {
"account": {
"id": "0a000000-0000-4000-a000-000000000001",
"name": "Northwind Analytics",
"owner": {
"id": "0d000000-0000-4000-a000-000000000001",
"name": "Dana Whitfield"
}
}
}
}
}3. Add the contacts in one batch
Each contact links to the account through account_relations, with its job title in
position. Exactly one relation per contact must have is_primary: true: with none, or with
two, the contact fails validation (field error code 117). A contact also needs first_name or
last_name.
contacts=$(curl -sS --fail-with-body "$COEVERA_BASE/entities/Contacts/batch-modify?rollback-method=1" \
-u "$COEVERA_API_KEY:$COEVERA_API_SECRET" \
-H "Content-Type: application/json" \
-d @- <<JSON
[
{
"first_name": "Ava", "last_name": "Lindqvist",
"email1": "ava.lindqvist@example.com",
"owner_id": "$OWNER_ID",
"account_relations": [
{ "account_id": "$ACCOUNT_ID", "position": "Head of Data", "is_primary": true }
]
},
{
"first_name": "Marco", "last_name": "Ruiz",
"email1": "marco.ruiz@example.com",
"owner_id": "$OWNER_ID",
"account_relations": [
{ "account_id": "$ACCOUNT_ID", "position": "CFO", "is_primary": true }
]
}
]
JSON
)
echo "$contacts"
# With rollback-method=1 a failed contact comes back as an error object in data: check every item.
failed=$(jq '[.data[] | objects] | length' <<<"$contacts")
if [ "$failed" -ne 0 ]; then
echo "$failed contact(s) failed" >&2
exit 1
ficonst batch = await coevera("/entities/Contacts/batch-modify?rollback-method=1", {
method: "POST",
body: [
{
first_name: "Ava",
last_name: "Lindqvist",
email1: "ava.lindqvist@example.com",
owner_id: ownerId,
account_relations: [{ account_id: accountId, position: "Head of Data", is_primary: true }],
},
{
first_name: "Marco",
last_name: "Ruiz",
email1: "marco.ruiz@example.com",
owner_id: ownerId,
account_relations: [{ account_id: accountId, position: "CFO", is_primary: true }],
},
],
});
// With rollback-method=1 a failed contact comes back as an error object in data: check every item.
const failed = batch.data.filter((item: unknown) => typeof item !== "string");
if (failed.length) throw new Error(`${failed.length} contact(s) failed: ${JSON.stringify(failed)}`);
console.log("Contacts:", batch.data);batch = coevera(
"POST",
"/entities/Contacts/batch-modify",
params={"rollback-method": 1},
json=[
{
"first_name": "Ava",
"last_name": "Lindqvist",
"email1": "ava.lindqvist@example.com",
"owner_id": owner_id,
"account_relations": [
{"account_id": account_id, "position": "Head of Data", "is_primary": True}
],
},
{
"first_name": "Marco",
"last_name": "Ruiz",
"email1": "marco.ruiz@example.com",
"owner_id": owner_id,
"account_relations": [{"account_id": account_id, "position": "CFO", "is_primary": True}],
},
],
)
# With rollback-method=1 a failed contact comes back as an error object in data: check every item.
failed = [item for item in batch["data"] if not isinstance(item, str)]
if failed:
sys.exit(f"{len(failed)} contact(s) failed: {json.dumps(failed)}")
print("Contacts:", batch["data"])contacts=$(jq -n --arg owner "$OWNER_ID" --arg account "$ACCOUNT_ID" '{
query: "mutation AddContacts($input: [CreateOrUpdateContactInput!]!) { bulkUpdateContact(input: $input, rollbackMethod: ROLLBACK_FAILED) { result { created { index id } errors { index code message } } } }",
variables: {
input: [
{
firstName: "Ava", lastName: "Lindqvist",
email1: "ava.lindqvist@example.com",
ownerId: $owner,
accountRelations: [{ accountId: $account, position: "Head of Data", isPrimary: true }]
},
{
firstName: "Marco", lastName: "Ruiz",
email1: "marco.ruiz@example.com",
ownerId: $owner,
accountRelations: [{ accountId: $account, position: "CFO", isPrimary: true }]
}
]
}
}' | gql)
echo "$contacts"
failed=$(jq '.data.bulkUpdateContact.result.errors | length' <<<"$contacts")
if [ "$failed" -ne 0 ]; then
echo "$failed contact(s) failed" >&2
exit 1
ficonst { data: added } = await gql(
`mutation AddContacts($input: [CreateOrUpdateContactInput!]!) {
bulkUpdateContact(input: $input, rollbackMethod: ROLLBACK_FAILED) {
result {
created { index id }
errors { index code message }
}
}
}`,
{
input: [
{
firstName: "Ava",
lastName: "Lindqvist",
email1: "ava.lindqvist@example.com",
ownerId,
accountRelations: [{ accountId, position: "Head of Data", isPrimary: true }],
},
{
firstName: "Marco",
lastName: "Ruiz",
email1: "marco.ruiz@example.com",
ownerId,
accountRelations: [{ accountId, position: "CFO", isPrimary: true }],
},
],
},
);
const { created: contactIds, errors } = added.bulkUpdateContact.result;
if (errors.length) throw new Error(`${errors.length} contact(s) failed: ${JSON.stringify(errors)}`);
console.log("Contacts:", contactIds);added = gql(
"""
mutation AddContacts($input: [CreateOrUpdateContactInput!]!) {
bulkUpdateContact(input: $input, rollbackMethod: ROLLBACK_FAILED) {
result {
created { index id }
errors { index code message }
}
}
}
""",
{
"input": [
{
"firstName": "Ava",
"lastName": "Lindqvist",
"email1": "ava.lindqvist@example.com",
"ownerId": owner_id,
"accountRelations": [
{"accountId": account_id, "position": "Head of Data", "isPrimary": True}
],
},
{
"firstName": "Marco",
"lastName": "Ruiz",
"email1": "marco.ruiz@example.com",
"ownerId": owner_id,
"accountRelations": [{"accountId": account_id, "position": "CFO", "isPrimary": True}],
},
]
},
)
result = added["bulkUpdateContact"]["result"]
if result["errors"]:
sys.exit(f"{len(result['errors'])} contact(s) failed: {json.dumps(result['errors'])}")
print("Contacts:", result["created"]){
"success": true,
"data": [
"0c000000-0000-4000-a000-000000000001",
"0c000000-0000-4000-a000-000000000002"
]
}{
"data": {
"bulkUpdateContact": {
"result": {
"created": [
{
"index": 0,
"id": "0c000000-0000-4000-a000-000000000001"
},
{
"index": 1,
"id": "0c000000-0000-4000-a000-000000000002"
}
],
"errors": []
}
}
}
}Warning
Without rollback-method=1, one failed contact fails the whole batch and nothing is written:
REST answers 422. In GraphQL, a bulkUpdateContact without rollbackMethod uses
ROLLBACK_ALL and answers HTTP 200 with errors and no data. Send ROLLBACK_FAILED to get
per-item results. Batch operations shows both
modes.
4. Read them back
Filter the contacts by the account they belong to, and check them here rather than in the batch
response. REST filters on account_relations.account_id; GraphQL on
accountRelations: { accountId: … }.
curl -sS --fail-with-body -G "$COEVERA_BASE/entities/Contacts" \
--data-urlencode "filter[account_relations.account_id]=$ACCOUNT_ID" \
-d load-only=id,first_name,last_name,email1 \
-u "$COEVERA_API_KEY:$COEVERA_API_SECRET"const contacts = await coevera(
`/entities/Contacts?filter[account_relations.account_id]=${accountId}&load-only=id,first_name,last_name,email1`,
);
console.log(contacts.total, "contacts on the account:", JSON.stringify(contacts.data, null, 2));contacts = coevera(
"GET",
"/entities/Contacts",
params={
"filter[account_relations.account_id]": account_id,
"load-only": "id,first_name,last_name,email1",
},
)
print(contacts.get("total"), "contacts on the account:", json.dumps(contacts["data"], indent=2))jq -n --arg account "$ACCOUNT_ID" '{
query: "query ReadBack($account: String!) { entities { contact { getByCriteria(filter: { accountRelations: { accountId: { eq: [$account] } } }) { totalCount edges { node { id firstName lastName email1 } } } } } }",
variables: { account: $account }
}' | gqlconst { data: readBack } = await gql(
`query ReadBack($accountId: String!) {
entities {
contact {
getByCriteria(filter: { accountRelations: { accountId: { eq: [$accountId] } } }) {
totalCount
edges { node { id firstName lastName email1 } }
}
}
}
}`,
{ accountId },
);
const contacts = readBack.entities.contact.getByCriteria;
console.log(contacts.totalCount, "contacts on the account:", JSON.stringify(contacts.edges, null, 2));read_back = gql(
"""
query ReadBack($accountId: String!) {
entities {
contact {
getByCriteria(filter: { accountRelations: { accountId: { eq: [$accountId] } } }) {
totalCount
edges { node { id firstName lastName email1 } }
}
}
}
}
""",
{"accountId": account_id},
)
contacts = read_back["entities"]["contact"]["getByCriteria"]
print(contacts["totalCount"], "contacts on the account:", json.dumps(contacts["edges"], indent=2)){
"success": true,
"total": 2,
"page_info": {
"start_cursor": "WyIwYzAwMDAwMC0wMDAwLTQwMDAtYTAwMC0wMDAwMDAwMDAwMDEiXQ==",
"end_cursor": "WyIwYzAwMDAwMC0wMDAwLTQwMDAtYTAwMC0wMDAwMDAwMDAwMDIiXQ==",
"has_previous_page": false,
"has_next_page": false
},
"data": [
{
"id": "0c000000-0000-4000-a000-000000000001",
"email1": "ava.lindqvist@example.com",
"first_name": "Ava",
"last_name": "Lindqvist"
},
{
"id": "0c000000-0000-4000-a000-000000000002",
"email1": "marco.ruiz@example.com",
"first_name": "Marco",
"last_name": "Ruiz"
}
]
}{
"data": {
"entities": {
"contact": {
"getByCriteria": {
"totalCount": 2,
"edges": [
{
"node": {
"id": "0c000000-0000-4000-a000-000000000001",
"firstName": "Ava",
"lastName": "Lindqvist",
"email1": "ava.lindqvist@example.com"
}
},
{
"node": {
"id": "0c000000-0000-4000-a000-000000000002",
"firstName": "Marco",
"lastName": "Ruiz",
"email1": "marco.ruiz@example.com"
}
}
]
}
}
}
}
}Show all 29 linesShow less
What to check
- Step 1 returns exactly one user. An empty
data(GraphQLedges) means the e-mail is not a user of the space. - Step 2 answers 201 (GraphQL: 200), and
data.id(GraphQLcreateAccount.account.id) is the account ID the next steps use. - Step 3 answers 200 with
"success": true, anddataholds two IDs, in the order you sent the contacts. Withrollback-method=1, a failed contact makessuccessfalse and leaves an object withcode,nameandmessagein its place; the other contact is still written. In GraphQL,result.createdlists the written items andresult.errorsthe failed ones, each byindex. - Step 4 returns
total: 2(GraphQLtotalCount: 2). If it returns 0, the relation is missing: check theaccount_idyou sent. - Run the program twice and you get two accounts named Northwind Analytics and four contacts:
the API does not refuse duplicates. To make it safe to repeat, look the account up by
namefirst and reuse it.
Clean up
The program leaves the account and its two contacts in your space. To remove them, send their
IDs to POST /entities/Contacts/batch-delete and POST /entities/Accounts/batch-delete, then
delete their rows in ContactAccountRelations the same way.
Note
Deleting a contact or an account leaves its ContactAccountRelations rows active, still
pointing at the deleted records. Find them with filter[account_id] and delete them too.
Deletes are soft: deleted records stay readable with include-deleted=true.
Limits
- The program stops at the first error and does not retry.
- The API sets no batch size limit; a batch of 500 items is accepted.
- It creates one account. For many accounts, create them in one
Accounts/batch-modifycall first, then send their contacts in a second batch.