What a VOR-Ready feed looks like.
AssetDNA turns a property system's day-to-day records into a verifiable operating record — an append-only, independently checkable history a lender or valuer can re-verify without trusting either of us. This page is the whole inbound contract: assetdna.connector.v1. Build against it, check yourself below, and your feed is VOR-Ready — no meeting required.
Check your feed
Paste a batch. The validator below is the same code our adapter runs, so a pass here is a pass in production — and it runs in your browser, so you can test with real data before any data-sharing agreement exists.
Your system already produces an export — a tenancy list, a bookings file, a rent roll, a payments run. Mapping one onto connector.v1 is a short piece of work we do with you, against the contract published on this page, and you keep the result. Send us a header line and we will tell you what it takes.
Everything below needs none of that: paste a connector.v1 batch and the same validator our adapter runs checks it in this browser — no account, no upload, no agreement.
Checked entirely in this browser — nothing is sent to AssetDNA. Paste a real batch if you like.
The envelope
| Field | Type | Meaning |
|---|---|---|
| format | "assetdna.connector.v1" | Format discriminator. Version-suffixed; a breaking change mints a new value and both stay accepted. |
| source | string | Your system's registered slug. Determines the corroboration grade the facts earn — see the source table below. An unregistered source still validates; it is graded conservatively until we classify it together. |
| externalPropertyId | string | The property's id in YOUR system. We map it to ours at connection time, so you never need to hold an AssetDNA identifier. |
| generatedAt | string (ISO-8601) | When you produced the batch. Must carry an explicit offset (Z or ±hh:mm) — a timestamp without one is ambiguous, and this is a record of when things happened. |
| nextCursor | string | null | Opaque cursor to the next page, or null/omitted when the set is complete. Batches are capped at 1000 records per array. |
| leases | object[] | Current lease roster: id, unitLabel, tenantName, startDate, endDate, rentAmount, rentPeriod, bondAmount, status. |
| transactions | object[] | Financial movements: id, type, amount, date, unitLabel. |
format, source, externalPropertyId and generatedAt are required on every batch; at least one of leases or transactions must be present. The authoritative definition is the JSON Schema (draft 2020-12).
Four rules that shape everything else
The contract is the payload, not the endpoint. Satisfy it over a REST endpoint we poll, a scheduled file drop — or push it yourself: POST a batch to /api/v1/ingest with an org API key (Authorization: Bearer, or x-assetdna-key). The declared source must match a connector the operator has registered in-app — a payload can never enrol its own source — and replaying a batch appends nothing (ids are the idempotency key).
Every record carries a stable id from your system. Re-sending a batch is safe: an id we already hold updates the record rather than duplicating it. Ids must be unique within one batch — a repeat means the sender can't say which is current, so the validator flags it.
The record is append-only. A correction is a new event that supersedes an earlier one; the earlier one stays visible with its original timestamp. If your system hard-deletes, send the correction — don't stop sending the row.
Passing this check means your feed is well-formed and machine-consumable. It does not mean the facts are true, and a property system's feed never counts as independent corroboration of its own assertions. That honesty is the product; see the grading table below.
What your records become
Each record maps to exactly one typed ledger event. A transaction type we don't recognise falls back to GL_TRANSACTION_SYNCED rather than being dropped — unmapped data is a conversation, never a silent loss.
LEASE_SYNCEDRENT_RECEIVEDBOND_HELDBOND_RELEASEDMAINTENANCE_INVOICEDGL_TRANSACTION_SYNCEDHow your facts get graded
Everything we receive is sealed on receipt — hash-chained, so it can't be altered afterwards, by you or by us. But sealing proves “unchanged since we took it”, never “true at source”. So a fact is separately graded by who is asserting it: a property system's own feed cannot corroborate the property system's own claim, no matter how clean the JSON is. A bank feed or a meter can.
| source | Kind | Independent corroboration? |
|---|---|---|
| yardi | PMS | No — sealed, but the system's own assertion |
| yardi_commercial | PMS | No — sealed, but the system's own assertion |
| starrez | PMS | No — sealed, but the system's own assertion |
| mews | PMS | No — sealed, but the system's own assertion |
| xero | FINANCIAL | Yes — independent of the property system |
| bank_feed | FINANCIAL | Yes — independent of the property system |
| iot_meters | IOT | Yes — independent of the property system |
| bms | BMS | Yes — independent of the property system |
| backoffice | OWNER | Yes — independent of the property system |
| external | EXTERNAL | Yes — independent of the property system |
| pms_generic | PMS | No — sealed, but the system's own assertion |
Not listed? Send the feed anyway. It validates and seals exactly the same; it is simply graded conservatively until we classify the source together during onboarding.
Versioning promise
assetdna.connector.v1 is frozen: fields may be added, never removed or changed in meaning. A breaking change mints assetdna.connector.v2 alongside it, and v1 feeds keep being accepted — a partner should never have to re-integrate because we changed our mind.