Bulk import
When you're migrating from another platform, seeding staging with a known list of customers, or onboarding a batch of new tenants at once, use the bulk-import endpoint. It creates up to 1000 tenants in a single request and reports per-row status (created vs skipped due to a pre-existing ID).
From the dashboard
- Open Tenants and click Bulk import in the top-right.
- Choose the format — CSV or JSON.
- Either click the upload area (or drag-and-drop) a
.csv/.jsonfile (up to 5 MB), or paste the content directly into the editor below. - If you're using CSV, click Download CSV template for the expected header row.
- Review the parse preview — the modal shows how many rows are ready to import and flags any parse errors.
- Click Import. The modal closes and a toast reports the result —
Imported N tenants(withM skipped (already existed)if any collided on ID).
CSV format
Headers required: id. Optional: name, metadata. Wrap metadata in double quotes because it contains commas — the JSON is what's stored:
id,name,metadata
acme-corp,Acme Corp,"{""tier"":""gold"",""region"":""wnam""}"
globex-inc,Globex Inc,"{""tier"":""silver""}"
initech,Initech,JSON format
A top-level array of tenant objects:
[
{ "id": "acme-corp", "name": "Acme Corp", "metadata": { "tier": "gold" } },
{ "id": "globex-inc", "name": "Globex Inc" },
{ "id": "initech" }
]From the API
curl --location 'https://api.truto.one/tenant/bulk' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <your-api-token>' \
--data '{
"tenants": [
{ "id": "acme-corp", "name": "Acme Corp", "metadata": {"tier":"gold"} },
{ "id": "globex-inc", "name": "Globex Inc" },
{ "id": "initech" }
]
}'Response (201 Created):
{
"created": [
{ "id": "acme-corp", "environment_id": "…", "name": "Acme Corp", "metadata": {"tier":"gold"}, "created_at": "…", "updated_at": "…" },
{ "id": "globex-inc", "environment_id": "…", "name": "Globex Inc", "metadata": {}, "created_at": "…", "updated_at": "…" },
{ "id": "initech", "environment_id": "…", "name": "initech", "metadata": {}, "created_at": "…", "updated_at": "…" }
],
"skipped": []
}Rows in skipped collided on (id, environment_id) — a tenant with that ID already existed. The endpoint uses INSERT ... ON CONFLICT DO NOTHING, so the request never fails partway through: whichever rows can be inserted are, and the rest are reported back to you.
Caps: at most 1000 tenants per request. Split larger inputs client-side and call POST /tenant/bulk multiple times.
From the CLI
# Inline
truto tenants create-bulk -b '[{"id":"acme"},{"id":"globex"}]'
# From an NDJSON file (one JSON object per line)
cat tenants.ndjson | truto tenants create-bulk --stdin
# From a JSON array file
truto tenants create-bulk -b "$(cat tenants.json)"The CLI enforces the 1000-row cap client-side so you catch it before hitting the network. See truto tenants.
Idempotency and re-runs
Because the endpoint skips existing IDs rather than failing, re-running the same import is safe: rerunning after adding a few new entries returns only those new entries in created and the rest in skipped. This makes it usable as a "sync my customer list" job on a cron.
If you need "upsert" semantics — update name / metadata on rows that already exist — do that separately with PATCH /tenant/:id after the bulk create returns its skipped list.