Reference ยท v1 Preview

API reference

Every v1 endpoint, honestly labeled. Live endpoints work today with an API key; Planned endpoints are on the published roadmap and return 404 for now.

Using an AI assistant? 🤖

Our MCP server exposes all 50 live v1 endpoints as tools for Claude Desktop, Muse, and other MCP clients, with full CRUD and confirm:true protection on destructive actions. Set up the Tenaro MCP server →

Authentication

Every request carries your key in the Authorization header. Keys are workspace-scoped. A key can only ever see its own workspace.

# Header format
Authorization: Bearer tnr_yourworkspace_<48-hex-chars>
๐Ÿ”‘

Key format

tnr_ + workspace slug + _ + 48 hex chars. Only the SHA-256 hash is stored. We can't recover a lost key, only revoke and reissue.

๐ŸŽฏ

Scopes

Keys are hierarchical: read < write < delete < admin. GET needs read; POST/PUT/PATCH need write; DELETE needs delete; user invite/disable needs admin. Request your level in the dashboard (Settings โ†’ API access). The owner approves it. admin is never self-serviceable.

โฑ๏ธ

Rate limits

600 requests/minute per key. Exceed it and you'll get 429 with a Retry-After header. Failed-auth attempts are limited per IP.

Pagination

Every list endpoint is cursor-paginated. Never assume a list is complete after one call.

# Request
GET /api/v1/properties?limit=2

# Response
{
  "data": [ โ€ฆ ],
  "pagination": {
    "has_more": true,
    "next_cursor": "42",
    "limit": 2
  }
}
# Next page: GET /api/v1/properties?limit=2&starting_after=42

Authentication

GET https://tenaro.io/api/v1/ โ— Live

API index

Discovery: version, workspace, your key's scopes, and the full live endpoint catalog.

Properties & Units

GET https://tenaro.io/api/v1/properties โ— Live

List properties

Paginated list of properties in the authenticated workspace. Filter by type, city, state, or country.

Query parameters

NameTypeDescription
limitinteger1โ€“100, default 20
starting_afterstringCursor from pagination.next_cursor
property_typestringe.g. single_family, multi_family, condo
citystringExact match
statestringExact match
countrystringExact match
{
  "data": [
    {
      "id": "7",
      "name": "Maple Apartments",
      "property_type": "multi_family",
      "address": {
        "line1": "123 Maple St", "city": "Austin",
        "state": "TX", "zip": "78701",
        "formatted": "123 Maple St, Austin, TX 78701"
      },
      "total_units": 12,
      "unit_count": 12
    }
  ],
  "pagination": { "has_more": false, "next_cursor": null, "limit": 20 }
}
GET https://tenaro.io/api/v1/properties/{id} โ— Live

Get property

Full detail for one property, including formatted address and unit count.

{
  "data": [
    {
      "id": "7",
      "name": "Maple Apartments",
      "property_type": "multi_family",
      "address": { โ€ฆ },
      "year_built": 1998,
      "total_units": 12,
      "purchase_price": 1450000.0,
      "created_at": "2026-03-14 10:22:31",
      "updated_at": "2026-09-30 08:11:02"
    }
  ]
}
POST https://tenaro.io/api/v1/properties โ— Live

Create property

Scope: write. Requires: name. JSON body.

{
  "data": [
    {
      "id": "7",
      "name": "Maple Apartments",
      "property_type": "multi_family",
      "address": { โ€ฆ },
      "year_built": 1998,
      "total_units": 12,
      "purchase_price": 1450000.0,
      "created_at": "2026-03-14 10:22:31",
      "updated_at": "2026-09-30 08:11:02"
    }
  ]
}
PUT https://tenaro.io/api/v1/properties/{id} โ— Live

Replace property

Scope: write. Full field set required.

{
  "data": [
    {
      "id": "7",
      "name": "Maple Apartments",
      "property_type": "multi_family",
      "address": { โ€ฆ },
      "year_built": 1998,
      "total_units": 12,
      "purchase_price": 1450000.0,
      "created_at": "2026-03-14 10:22:31",
      "updated_at": "2026-09-30 08:11:02"
    }
  ]
}
PATCH https://tenaro.io/api/v1/properties/{id} โ— Live

Update property

Scope: write. Partial update.

{
  "data": [
    {
      "id": "7",
      "name": "Maple Apartments",
      "property_type": "multi_family",
      "address": { โ€ฆ },
      "year_built": 1998,
      "total_units": 12,
      "purchase_price": 1450000.0,
      "created_at": "2026-03-14 10:22:31",
      "updated_at": "2026-09-30 08:11:02"
    }
  ]
}
DELETE https://tenaro.io/api/v1/properties/{id} โ— Live

Delete property

Scope: delete. Hard delete (units cascade).

GET https://tenaro.io/api/v1/units โ— Live

List units

Filter by property_id or status.

Query parameters

NameTypeDescription
property_idintegerExact match
statusstringExact match
limitinteger1โ€“100, default 20
starting_afterstringCursor
GET https://tenaro.io/api/v1/units/{id} โ— Live

Get unit

One unit.

POST https://tenaro.io/api/v1/units โ— Live

Create unit

Scope: write. Requires: property_id, unit_label.

PATCH https://tenaro.io/api/v1/units/{id} โ— Live

Update unit

Scope: write. Partial update.

DELETE https://tenaro.io/api/v1/units/{id} โ— Live

Delete unit

Scope: delete.

Tenants

GET https://tenaro.io/api/v1/tenants โ— Live

List tenants

Search by name/email; filter by is_active.

Query parameters

NameTypeDescription
searchstringMatches name/email
is_activebooleanFilter by active flag
limitinteger1โ€“100, default 20
starting_afterstringCursor
GET https://tenaro.io/api/v1/tenants/{id} โ— Live

Get tenant

One tenant (safe fields).

POST https://tenaro.io/api/v1/tenants โ— Live

Create tenant

Scope: write. Requires: email. A temporary password is generated and returned ONCE if omitted.

PATCH https://tenaro.io/api/v1/tenants/{id} โ— Live

Update tenant

Scope: write. Partial update.

DELETE https://tenaro.io/api/v1/tenants/{id} โ— Live

Deactivate tenant

Scope: delete. Soft-delete: leases and history are preserved.

Vendors

GET https://tenaro.io/api/v1/vendors โ— Live

List vendors

Vendor directory with profiles. Filter by search text or trade.

Query parameters

NameTypeDescription
searchstringMatches name/email/company
tradestringMatches trade_expertise
limitinteger1โ€“100, default 20
starting_afterstringCursor
GET https://tenaro.io/api/v1/vendors/{id} โ— Live

Get vendor

One vendor with profile.

POST https://tenaro.io/api/v1/vendors โ— Live

Create vendor

Scope: write. Requires: email. Optional profile fields (company_name, trade_expertise, hourly_rate, โ€ฆ).

PATCH https://tenaro.io/api/v1/vendors/{id} โ— Live

Update vendor

Scope: write. Updates user and profile fields.

DELETE https://tenaro.io/api/v1/vendors/{id} โ— Live

Deactivate vendor

Scope: delete. Soft-delete: job history preserved.

GET https://tenaro.io/api/v1/vendor-messages โ— Live

List vendor messages

Filter by vendor_user_id or channel.

GET https://tenaro.io/api/v1/vendor-messages/{id} โ— Live

Get vendor message

One message.

POST https://tenaro.io/api/v1/vendor-messages โ— Live

Log vendor message

Scope: write. Requires: vendor_user_id, channel.

Leases

GET https://tenaro.io/api/v1/leases โ— Live

List leases

Filter by unit_id, tenant_user_id, or status.

Query parameters

NameTypeDescription
unit_idintegerExact match
tenant_user_idintegerExact match
statusstringExact match
limitinteger1โ€“100, default 20
starting_afterstringCursor
GET https://tenaro.io/api/v1/leases/{id} โ— Live

Get lease

One lease.

POST https://tenaro.io/api/v1/leases โ— Live

Create lease

Scope: write. Requires: unit_id, tenant_user_id, start_date, end_date (YYYY-MM-DD), rent_amount.

PATCH https://tenaro.io/api/v1/leases/{id} โ— Live

Update lease

Scope: write. Partial update.

DELETE https://tenaro.io/api/v1/leases/{id} โ— Live

Delete lease

Scope: delete. Payments cascade.

Payments & Accounting

GET https://tenaro.io/api/v1/payments โ— Live

List payments

Filter by lease_id or status.

Query parameters

NameTypeDescription
lease_idintegerExact match
statusstringExact match
limitinteger1โ€“100, default 20
starting_afterstringCursor
GET https://tenaro.io/api/v1/payments/{id} โ— Live

Get payment

One payment record.

POST https://tenaro.io/api/v1/payments โ— Live

Record payment

Scope: write. Requires: lease_id, amount (>0), due_date (YYYY-MM-DD). Payments cannot be deleted via the API.

PATCH https://tenaro.io/api/v1/payments/{id} โ— Live

Update payment

Scope: write. Update status / paid_at / payment_method_id.

GET https://tenaro.io/api/v1/documents โ— Live

List documents

Property document metadata (read-only). Filter by property_id or category.

GET https://tenaro.io/api/v1/documents/{id} โ— Live

Get document

One document's metadata.

Maintenance

GET https://tenaro.io/api/v1/maintenance โ— Live

List maintenance requests

Filter by property_id, status, priority, assigned_vendor_user_id.

Query parameters

NameTypeDescription
property_idintegerExact match
statusstringExact match
prioritystringExact match
assigned_vendor_user_idintegerExact match
limitinteger1โ€“100, default 20
starting_afterstringCursor
GET https://tenaro.io/api/v1/maintenance/{id} โ— Live

Get maintenance request

One request.

POST https://tenaro.io/api/v1/maintenance โ— Live

Create request

Scope: write. Requires: property_id, unit_id, tenant_user_id, title.

PATCH https://tenaro.io/api/v1/maintenance/{id} โ— Live

Update request

Scope: write. Status changes write a status-history entry (optional status_note).

DELETE https://tenaro.io/api/v1/maintenance/{id} โ— Live

Delete request

Scope: delete.

Users (admin)

GET https://tenaro.io/api/v1/users โ— Live

List users

Safe fields only. Filter by role or search.

GET https://tenaro.io/api/v1/users/{id} โ— Live

Get user

One user (safe fields).

POST https://tenaro.io/api/v1/users/invite โ— Live

Invite user

Scope: admin. Role must be PROPERTY_MANAGER, TENANT, or VENDOR: OWNER/ADMIN cannot be created via API.

POST https://tenaro.io/api/v1/users/{id}/disable โ— Live

Disable user

Scope: admin. OWNER/ADMIN accounts are protected.

POST https://tenaro.io/api/v1/users/{id}/enable โ— Live

Enable user

Scope: admin.

Campaigns

GET https://tenaro.io/api/v1/campaigns โ— Live

List campaigns

Filter by status or mode.

GET https://tenaro.io/api/v1/campaigns/{id} โ— Live

Get campaign

One campaign.

POST https://tenaro.io/api/v1/campaigns โ— Live

Create campaign

Scope: write. Creates a DRAFT. Requires: subject, body. Sending stays dashboard-only.

PATCH https://tenaro.io/api/v1/campaigns/{id} โ— Live

Update campaign

Scope: write. DRAFT campaigns only.

DELETE https://tenaro.io/api/v1/campaigns/{id} โ— Live

Delete campaign

Scope: delete. DRAFT campaigns only.

Roadmap

POST https://tenaro.io/api/v1/oauth/token โ—‹ Planned

Exchange OAuth code

For third-party apps.

GET https://tenaro.io/api/v1/invoices โ—‹ Planned

List invoices

Rent invoices and late fees.

GET https://tenaro.io/api/v1/reports/rent-roll โ—‹ Planned

Rent roll report

GET https://tenaro.io/api/v1/reports/delinquency โ—‹ Planned

Delinquency report

POST https://tenaro.io/api/v1/ai/ask โ—‹ Planned

Ask Tenaro AI

Natural-language query over your workspace.

Errors

One envelope for every failure. The request_id lets our team trace any call; docs links here.

{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key.",
    "request_id": "req_9f2ac41be077d311",
    "docs": "https://tenaro.io/developers-reference.php#errors"
  }
}
HTTPCodeMeaning
400bad_requestMalformed parameters.
401unauthorizedMissing, malformed, revoked, or expired key, or a key for a different workspace.
403forbiddenKey lacks the required scope.
404not_foundUnknown endpoint or resource id. Planned endpoints also return this until they ship.
405method_not_allowedMethod not supported for this endpoint (e.g. DELETE on payments).
422invalid_requestValidation failed: missing/invalid fields. The message names the field.
429rate_limitedSlow down; honor Retry-After.
500internal_errorSomething broke on our side. Retry with backoff and contact us with the request_id.

Changelog & versioning

v1 preview (2026-10-01)

Initial release. Live: GET /api/v1/properties, GET /api/v1/properties/{id}. Bearer auth, cursor pagination, rate limits.

Full workspace control (2026-10-02)

50 live endpoints: full CRUD for properties, units, tenants, vendors, leases, maintenance, payments, campaigns, vendor messages; read for users, documents; admin user invite/disable. Hierarchical scopes (read/write/delete/admin) with self-service requests and owner approval. Every mutation is audit-logged. MCP server mirrors all 50 endpoints.

Deprecation policy

Breaking changes get a new version (/v2/), never a silent change to /v1/. Deprecated versions receive 12 months' notice.

MCP server

Model Context Protocol server for Claude Desktop, Muse, and other MCP clients: 50 tools mirroring every live v1 endpoint, full CRUD. Destructive tools require an explicit confirm:true argument. Setup guide →