Integration-Sourced Data
Build idempotent batch writes and handle irreversible provider work
Mental Model
Integration writes connect stable external identity to VRPlatform resources. Database mutations and external provider actions have different guarantees and must be presented separately.
Resources and Lifecycle
Connection-scoped writes can use connectionId, sourceId, uniqueRef, and
an existing API id to resolve create versus update. Syncs and provider calls
continue asynchronously after their initiating request.
Bank-record batches prefer an exact sourceId match. If an importer rotates
that source row, the same uniqueRef on the same bank account still resolves
the existing bank record and updates its source identity instead of creating a
duplicate.
Read Model
Render batch data[] and item-level issues[], returned platform IDs, source
identity, sync state, and operation progress. Do not infer that one accepted
batch means every item or external action completed.
Decision Table
| Situation | Rule |
|---|---|
| External identity | Stable inside its documented connection scope |
| Display name | Never use as identity |
| Dry-run ID | Discard; it was rolled back |
| Relationship array | Send complete set only for documented replace semantics |
| Database batch | Dry-run only when OpenAPI declares support |
| OAuth, upload, sync, invitation, email | No generic dry run |
| Browser-extension credentials | Require stable unique ref, email, or username |
| Built-in API connection | Server assigns opaque identity; reconnect preserves it |
| Migration or cutover | Use dedicated preview and apply workflow |
Editability
Source ownership can make selected fields read-only. Team scope, connection scope, permissions, and existing journal locks still apply to imported resources. Never overwrite local fields merely because the external record has the same display name.
Preview and Preflight
Database-backed contact, listing, reservation, transaction, bank-account, and
bank-record batches take dryRun wherever the generated OpenAPI declares it.
Provider operations cannot promise rollback. Dedicated migration preview
endpoints report affected rows, locks, mappings, and cutover consequences.
For general-ledger teams, each new listing also receives its zero-value opening balance transaction during the listing batch. Journal materialization is queued after the response, so re-read accounting state instead of assuming those journal entries exist when the listing response arrives.
Mutation Recipe
- Resolve team and connection.
- Normalize and preserve stable source identifiers.
- Load existing mappings or platform IDs.
- Validate every item and relationship set.
- Dry-run a supported database batch or request dedicated preview.
- Present item outcomes and blockers.
- Apply with the same identity and payload.
- Persist returned platform IDs only from the real write.
- Monitor asynchronous sync or provider state.
Failure and Recovery
Retry idempotently with the same external identity. Repair failed items from their structured issues instead of renaming references. For an uncertain provider result, read current connection or sync state before issuing another external action.
Provider integrations must return stable account identity when credentials are completed. The built-in API app has no external account, so VRPlatform assigns an opaque connection identity and retains it when that connection is reconnected.
Common Recipes
Upsert a reservation batch
Use stable connection-scoped references, dry-run the exact batch, resolve each item issue, apply, then store real reservation IDs and render calculated state.
POST /reservations/batch?dryRun=true
Content-Type: application/json
{
"connectionId": "77777777-7777-4777-8777-777777777777",
"data": [
{
"uniqueRef": "pms-reservation-1842",
"listingId": "11111111-1111-4111-8111-111111111111",
"status": "booked",
"currency": "usd",
"guestName": "Alex Morgan",
"checkIn": "2026-08-14",
"checkOut": "2026-08-18",
"lines": [
{
"uniqueRef": "rent",
"type": "rent",
"description": "Accommodation",
"amount": 125000
}
]
}
]
}Start an OAuth connection
Use the connection flow, persist its state token server-side, handle the callback, and read the resulting connection. Do not offer a dry-run toggle.
POST /connections/connect
Content-Type: application/json
{
"appId": "<app-id-from-get-apps>",
"name": "Primary PMS",
"redirectTo": "https://example.com/integrations/callback",
"params": {}
}API Reference
- Import contacts — POST /contacts/batch
- Import listings — POST /listings/batch
- Import reservations — POST /reservations/batch
- Import transactions — POST /transactions/batch
- Import bank accounts — POST /bank-accounts/batch
- Import bank records — POST /bank-records/batch
- Connect an app — POST /connections/connect
