Help topics

API reference

Bulk import

Upload a rent roll as CSV. Each row creates the property, unit, tenant, lease and deposit in one call.

Download the template

GET/import/templatescope tenure:read

Returns text/csv with the column headings and two example rows.

Property,Unit,Unit Code,Unit Type,Tenant Name,Phone,WhatsApp,M-Pesa Number,Rent,Deposit,Start Date,Billing Day,Balance
Riverside Court,B12,B12,two_bedroom,Faith Wafula,0722145880,,0733902471,25000,25000,01/03/2025,1,0
Riverside Court,B14,B14,one_bedroom,John Mwangi,0733902471,,,18000,18000,15/06/2025,15,18000

Import tenancies

POST/import/tenanciesscope tenure:write

Query parameters

FieldTypeRequiredDescription
dry_runtrue | falsenoDefault true: validate every row, write nothing. Send false to commit.
property_iduuidnoProperty for rows with a blank Property cell. Use it for single-building files.

Body

JSON { "csv": "<file contents>" }, or the raw file with Content-Type: text/plain. Maximum 1 MB.

CSV columns

One row per occupied unit. Headings are case- and punctuation-insensitive, and common aliases are accepted (House No for Unit, Mobile for Phone).

ColumnRequiredFormat / default
UnityesDoor label, e.g. B12
Tenant NameyesFull name
PhoneyesKenyan mobile, any common format
RentyesMonthly, in shillings: 25000, 25,000 or KES 25,000
Propertyyes*Found by name or created. *Or pass property_id
Unit CodenoPaybill account reference. Default: the Unit label
Unit TypenoDefault one_bedroom
WhatsAppnoDefault: Phone
M-Pesa NumbernoThe number the tenant pays from, if different
DepositnoDeposit held, in shillings. Default 0
Start DatenoYYYY-MM-DD or DD/MM/YYYY. Default today
Billing Dayno1–31. Default 5
BalancenoOwed today, in shillings. Becomes one opening invoice
ID NumbernoNational ID

Example request

curl -X POST "https://api.astralyngroup.com/tenure/v1/import/tenancies?dry_run=true" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"csv":"Property,Unit,Tenant Name,Phone,Rent\nRiverside Court,B12,Faith Wafula,0722145880,25000\nRiverside Court,B14,John Mwangi,,18000"}'

Response

200 for a dry run or when nothing was written; 201 when at least one row was imported. Row problems never fail the request: read rows.

200 OK
{
  "dryRun": true,
  "totalRows": 2,
  "imported": 1,
  "alreadyImported": 0,
  "failed": 1,
  "rows": [
    { "line": 2, "outcome": "imported", "unitLabel": "B12", "tenantName": "Faith Wafula" },
    { "line": 3, "outcome": "failed", "unitLabel": "B14", "tenantName": "John Mwangi",
      "problem": "No phone number. Every tenant needs one — it is how they are matched and told." }
  ]
}
FieldTypeRequiredDescription
dryRunbooleanyesEchoes the dry_run you sent.
totalRowsintegeryesData rows in the file, excluding the header.
importedintegeryesRows created (or that would be, on a dry run).
alreadyImportedintegeryesRows skipped because the unit is already let.
failedintegeryesRows with a problem. Nothing was written for them.
rows[].lineintegeryesSpreadsheet line number; the header is line 1.
rows[].outcomestringyesimported | already_imported | failed
rows[].problemstringnoWhy the row failed, in plain words.
rows[].leaseIduuidnoThe new lease. Only when committed.
fileProblemstringnoThe whole file is unusable (no rows, missing required column). rows is empty.

Behaviour

  • Validate first. Call with dry_run=true, fix the failed rows, then call again with dry_run=false.
  • Safe to re-send. Units are matched by Unit Code and tenants by phone. A unit that already has a live lease is reported as already_imported, never duplicated.
  • Partial success. Each row commits on its own; one bad row does not block the rest.
  • Unit Codes must be unique within the file. M-Pesa payments are matched on them.
  • No messages are sent. Imported tenants hear from Nyumbisha at their next rent date.
  • No payment history. A Balance becomes one opening invoice; past receipts are not recreated.