Zoodealio

Developers

Build on Zoodealio

Submit properties, run AI comping and take offers from estimate to accepted. The same steps as the app, from your own code or your AI assistant. These docs are public; calling the API needs a personal API key.

Search the developer docs

Find an endpoint, section, term or question.

Get an API keyDocs for LLMs

Quickstart: the whole journey

Take a property from submission to an accepted offer, in the same steps the app walks you through.

  1. 1

    Get your API key

    Open Settings → API in the Zoodealio app and generate a key. It is shown once, so keep it in a secret manager or an environment variable, never in code.

    Shell

    export ZOODEALIO_API_KEY="zoo_…"
  2. 2

    Subscribe to webhooks

    Zoodealio tells you when each step finishes, so you never poll. transaction.stage_changed covers every step; the others mark the moments you act on.

    Shell

    curl -X POST "https://api.zoodealio.ai/openapi/Webhooks" \
      -H "ApiKey: $ZOODEALIO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"endpointUrl":"https://you.example/zoodealio","eventTypes":["transaction.stage_changed","offer.created","property.files_imported","comping.completed","comping.failed","offer.details_shared","offer.accepted"]}'
  3. 3

    Submit the property

    Send the address, the home’s details and the homeowner. Photos and documents ride along as URLs you host, as many as the listing has; Zoodealio imports them in the background. The property is priced on the spot and gets an estimated offer (offer.created).

    Shell

    curl -X POST "https://api.zoodealio.ai/openapi/Properties" \
      -H "ApiKey: $ZOODEALIO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"address":{"addressLine1":"102 Shore Drive","city":"Youngsville","stateCode":"LA","postalCode":"70592","country":"US"},"attributes":{"bedroomCount":4,"bathroomCount":2.5,"squareFootage":2400,"yearBuilt":2016},"homeowner":{"email":"jane.doe@example.com","fName":"Jane","lName":"Doe","phone":"+15551234567"},"photos":[{"url":"https://cdn.example.com/102-shore/front.jpg"}]}'

    Then wait for property.files_imported

  4. 4

    Show the estimate

    Read the property’s offers. The estimate is kind: "Estimated" and carries only its range, estimatedRange: { low, high }. Keep its id for the next step.

    Shell

    curl "https://api.zoodealio.ai/openapi/Offers?propertyId=$PROPERTY_ID" \
      -H "ApiKey: $ZOODEALIO_API_KEY"
  5. 5

    Accept the estimate: confirm details and start AI comping

    Accepting the estimate asks for real offers and starts AI comping. Before you do, have the homeowner confirm the home’s details and send them with it. Photos should already be in: send more first with POST /openapi/Properties/{id}/photos and wait for property.files_imported. Both are optional: accept with no body at all to skip them.

    Shell

    curl -X POST "https://api.zoodealio.ai/openapi/Offers/$ESTIMATE_ID/accept" \
      -H "ApiKey: $ZOODEALIO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"attributes":{"bedroomCount":4,"bathroomCount":3,"squareFootage":2450,"yearBuilt":2016}}'

    Then wait for comping.completed

  6. 6

    Show the offers

    When comping completes, read the offers again. Each real offer is kind: "Actual" with its headline amount; its terms stay null until the next step. revealUrl on the comping status opens the full reveal in the app.

    Shell

    curl "https://api.zoodealio.ai/openapi/Offers?propertyId=$PROPERTY_ID" \
      -H "ApiKey: $ZOODEALIO_API_KEY"
  7. 7

    Request the details

    Ask for the full terms of the offer the homeowner is interested in. Zoodealio finalizes it and shares the details.

    Shell

    curl -X POST "https://api.zoodealio.ai/openapi/Offers/$OFFER_ID/request-details" \
      -H "ApiKey: $ZOODEALIO_API_KEY"

    Then wait for offer.details_shared

  8. 8

    Accept the offer

    Read the offer to show its full terms, then accept it. The transaction moves to OfferSelected and contracting continues in the app.

    Shell

    curl -X POST "https://api.zoodealio.ai/openapi/Offers/$OFFER_ID/accept" \
      -H "ApiKey: $ZOODEALIO_API_KEY"

    Then wait for offer.accepted

Journey stages

A transaction's stage moves through these in order. Every change arrives as transaction.stage_changed.

StageMoved byWhat it means
EstimatedOfferAvailableYouProperty submitted and priced; the estimate is ready.
OfferRequestedYouEstimate accepted; AI comping is starting.
CompingQueuedZoodealioAI comping is queued.
CompingInProgressZoodealioAI comping is running.
CompingCompleteZoodealioReal offers are ready (comping.completed).
CompingViewedThe appSomeone opened the offer reveal.
DetailsRequestedYouFull details requested.
OfferReadyZoodealioThe offer is finalized.
DetailsSharedZoodealioFull terms are readable (offer.details_shared).
OfferSelectedYouOffer accepted (offer.accepted).

For AI agents

Everything an AI assistant needs to build on Zoodealio without guessing. None of it needs a key.

Docs for LLMs

The whole reference as one Markdown file. Paste the link into a chat, or point your agent at it.

https://zoodealio.ai/developers/llms-full.txt
Open in ChatGPTllms.txt index

Docs MCP server

Lets your coding assistant search these docs and read exact paths and fields.

claude mcp add --transport http --scope user zoodealio-docs https://zoodealio.ai/developers/mcp

Account MCP server

A separate server on the API host that acts as you, with your API key. Its tools are API calls, so an AI assistant takes the same steps: submit a property and its photos, accept the estimate to request AI comping, request details and accept an offer, and manage the team.

https://api.zoodealio.ai/openapi/mcp
Set up Claude, Cursor or VS Code

Agent skills

SKILL.md files that teach a coding assistant how to build on Zoodealio correctly. Install them all for Claude Code with one command, or download one (Cursor: ~/.cursor/skills/<name>/SKILL.md, Copilot: ~/.copilot/skills/<name>/SKILL.md).

for s in zoodealio-api zoodealio-webhooks zoodealio-offers; do mkdir -p ~/.claude/skills/$s && curl -fsSL "https://zoodealio.ai/developers/skills/$s/SKILL.md" -o ~/.claude/skills/$s/SKILL.md; done

zoodealio-api

Build integrations against the REST API: auth, the journey from property to accepted offer, and the rules that cause most bugs.

SKILL.md

zoodealio-webhooks

Receive webhooks safely: signature verification in Node and Python, retries, dedupe.

SKILL.md

zoodealio-offers

For AI assistants connected to the Zoodealio MCP server: take a property from estimate to accepted offer.

SKILL.md

Getting started

Every endpoint is listed as a full URL against this environment's API host. Requests and responses are JSON, and photos and documents are sent as URLs.

Base URL

https://api.zoodealio.ai/openapi

Example request

curl "https://api.zoodealio.ai/openapi/Properties?page=1&pageSize=20" \
  -H "ApiKey: zoo_your_key_here"

curl "https://api.zoodealio.ai/openapi/Offers?propertyId=5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a" \
  -H "ApiKey: zoo_your_key_here"

One base URL

Every request goes to the same API host under /openapi/<Resource>. There is one version of the API: no version segments and no duplicate routes. The only other URLs involved are your own webhook endpoints, which Zoodealio calls back.

Authentication

Send your API key in the ApiKey header on every request (Authorization: Bearer <key> also works). Keys look like zoo_… and are shown exactly once when generated or rotated, on Settings → API. The key belongs to you and uses your current role, team memberships and assignments, just like the app.

The same actions as the app

The API does what you can do in the app, and nothing more: submit and edit properties, add photos and documents, accept an estimate to start AI comping, request an offer’s details and accept an offer. Offers are made by Zoodealio, so they cannot be created or edited through the API.

What you see, and when

The same gate as the app. An estimate shows only its range. A real offer shows its headline amount once AI comping completes, and its full terms once you request details and they are shared. Internal notes, valuation evidence and comparable sales are never returned.

Teams and workspace moves

Owners and account admins have workspace access. Team admins have access through the teams they administer and every team below them. Members have access to their own homeowners and properties. Your key and webhook subscriptions stay with you when you move workspaces, and access is recalculated from your current workspace, including before each webhook delivery.

Ids in the path, not the body

Every update and delete addresses its record through the URL, e.g. PUT /openapi/Properties/{id}. Sending the id in the body against the collection path instead returns 405 Method Not Allowed, because the collection has no PUT.

Rate limiting

Requests over the limit return 429 Too Many Requests with a Retry-After header telling you how long to back off.

Errors

Failures come back as application/problem+json with a title and detail: 400 for validation, 401 for a bad key, 403 for an action your role cannot perform, 404 for a record outside your current scope, 409 for a conflict (a duplicate address, or a step taken out of order).

Status codes

Failures are application/problem+json with a title and detail. What each status means and what to do about it:

StatusMeaningWhat to do
200 / 201Success.Read the JSON body; a create returns the new id.
204Success with no body.Nothing to read.
400Validation failed.Fix the fields named in the problem detail; do not retry unchanged.
401Missing or invalid API key.Send ApiKey; rotate the key if it leaked.
403Your role cannot do this.Use a key whose owner has the permission.
404Not found, or outside your current access.Check the id and that the record belongs to someone you can see.
405Wrong method for this path.Put the id in the path, e.g. PUT /openapi/Properties/{id}.
409Conflict: a duplicate address, a stale If-Match, or a step taken out of order.Re-read the transaction’s stage and take the step it is waiting for.
413Upload too large.A multipart upload over about 4 MB. Send the files as URLs instead.
429Rate limited.Wait the number of seconds in Retry-After, then retry.

Properties

The core record. Submit a property with its homeowner, photos and documents, edit its details, and manage its files. Everything else hangs off a property.

Examples

POST /openapi/Properties: request

{
  "address": {
    "addressLine1": "102 Shore Drive",
    "addressLine2": "",
    "city": "Youngsville",
    "stateCode": "LA",
    "postalCode": "70592",
    "country": "US"
  },
  "attributes": {
    "bedroomCount": 4,
    "bathroomCount": 2.5,
    "squareFootage": 2400,
    "yearBuilt": 2016
  },
  "homeowner": {
    "email": "jane.doe@example.com",
    "fName": "Jane",
    "lName": "Doe",
    "phone": "+15551234567"
  },
  "photos": [
    { "url": "https://cdn.example.com/listings/102-shore/front.jpg", "label": "Front" },
    { "url": "https://cdn.example.com/listings/102-shore/kitchen.jpg", "label": "Kitchen" }
  ],
  "documents": [
    { "url": "https://cdn.example.com/listings/102-shore/disclosure.pdf" }
  ]
}

201 Created: response (Location header carries the new property URL)

{
  "id": "5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a",
  "homeownerId": "8a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
  "importId": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
  "importStatusUrl": "/openapi/Properties/5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a/imports/7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d"
}

GET /openapi/Properties/{id}: response

{
  "id": "5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a",
  "address": {
    "addressLine1": "102 Shore Drive",
    "addressLine2": "",
    "city": "Youngsville",
    "stateCode": "LA",
    "postalCode": "70592",
    "country": "US"
  },
  "attributes": {
    "bedroomCount": 4,
    "bathroomCount": 2.5,
    "squareFootage": 2400,
    "yearBuilt": 2016
  },
  "latitude": 30.108,
  "longitude": -91.987,
  "agentId": "3f6c1a2e-9b4d-4c1f-8a2b-1d2e3f4a5b6c",
  "homeownerId": "8a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
  "createdAt": "2026-09-24T12:00:00Z",
  "updatedAt": "2026-09-24T12:00:00Z",
  "eTag": "\"0x8DCB…\"",
  "activeTransactionId": "0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f"
}

PUT /openapi/Properties/{id}: request

{
  "address": {
    "addressLine1": "102 Shore Drive",
    "addressLine2": "",
    "city": "Youngsville",
    "stateCode": "LA",
    "postalCode": "70592",
    "country": "US"
  },
  "attributes": {
    "bedroomCount": 4,
    "bathroomCount": 3,
    "squareFootage": 2450,
    "yearBuilt": 2016
  }
}

POST /openapi/Properties/{id}/photos: request

{
  "photos": [
    { "url": "https://cdn.example.com/listings/102-shore/front.jpg", "label": "Front" },
    { "url": "https://cdn.example.com/listings/102-shore/kitchen.jpg", "label": "Kitchen" }
  ]
}

202 Accepted: response (the same for photos and documents)

{
  "importId": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
  "statusUrl": "/openapi/Properties/5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a/imports/7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d"
}

POST /openapi/Properties/{id}/documents: request

{
  "documents": [
    { "url": "https://cdn.example.com/listings/102-shore/disclosure.pdf" },
    { "url": "https://files.example.com/dl?id=8812", "fileName": "inspection.pdf" }
  ]
}

A few local files instead: multipart, saved during the request (about 4 MB)

curl -X POST "https://<services-host>/openapi/Properties/$id/photos" \
  -H "ApiKey: $ZOODEALIO_API_KEY" \
  -F "photos=@front.jpg" \
  -F "photos=@kitchen.jpg"

GET /openapi/Properties/{id}/imports/{importId}: response

{
  "importId": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
  "propertyId": "5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a",
  "status": "completed",
  "total": 62,
  "processed": 62,
  "photoIds": ["c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f", "…"],
  "documentIds": ["e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7a8b"],
  "failed": [
    {
      "source": "https://cdn.example.com/listings/102-shore/kitchen.jpg",
      "reason": "The URL answered 404."
    }
  ]
}

GET /openapi/Properties/{id}/documents: response

[
  {
    "id": "e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7a8b",
    "fileName": "disclosure.pdf",
    "size": 482113,
    "contentType": "application/pdf",
    "url": "https://…/property-documents/5d4c…/e5f6….pdf",
    "uploadedByUserId": "3f6c1a2e-9b4d-4c1f-8a2b-1d2e3f4a5b6c",
    "uploader": {
      "userId": "3f6c1a2e-9b4d-4c1f-8a2b-1d2e3f4a5b6c",
      "fName": "Alex",
      "lName": "Smith",
      "email": "alex.smith@example.com"
    },
    "createdAt": "2026-09-24T12:00:00Z"
  }
]

GET /openapi/Properties/{id}/photos: response

[
  {
    "id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
    "url": "https://…/property-photos/5d4c…/c4d5….webp",
    "expiresAt": "2026-09-24T13:00:00Z",
    "order": 0,
    "createdAt": "2026-09-24T12:00:00Z",
    "label": "Front"
  }
]

Offers

Read a property’s offers and take the two actions the app gives you: accept an offer, and request an offer’s full details.

Examples

GET /openapi/Offers?propertyId={propertyId}: response right after submission

{
  "items": [
    {
      "id": "9b8a7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
      "propertyId": "5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a",
      "transactionId": "0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
      "type": "CashOffer",
      "kind": "Estimated",
      "status": "Estimated",
      "disposition": "Available",
      "currentRevisionId": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
      "isAvailable": true,
      "isAccepted": false,
      "isExpired": false,
      "hasUnacceptedRevision": false,
      "expiresAt": null,
      "eTag": "\"0x8DCA…\"",
      "detailsShared": false,
      "amount": null,
      "estimatedRange": { "low": 267750, "high": 362250 },
      "cashOffer": null,
      "cashOfferPlus": null,
      "sellNowMoveLater": null,
      "listOnMarket": null
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 20
}

GET /openapi/Offers/{id}: an actual offer before details are shared

{
  "id": "1c2d3e4f-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
  "propertyId": "5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a",
  "transactionId": "0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "type": "CashOffer",
  "kind": "Actual",
  "status": "Sent",
  "disposition": "Available",
  "currentRevisionId": "7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a",
  "isAvailable": true,
  "isAccepted": false,
  "isExpired": false,
  "hasUnacceptedRevision": false,
  "expiresAt": "2026-10-08T12:00:00Z",
  "eTag": "\"0x8DCB…\"",
  "detailsShared": false,
  "amount": 318000,
  "estimatedRange": null,
  "cashOffer": null,
  "cashOfferPlus": null,
  "sellNowMoveLater": null,
  "listOnMarket": null
}

POST /openapi/Offers/{id}/accept: request (estimated offer; body optional)

{
  "attributes": {
    "bedroomCount": 4,
    "bathroomCount": 3,
    "squareFootage": 2450,
    "yearBuilt": 2016
  }
}

POST /openapi/Offers/{id}/accept: 200 OK response (the transaction)

{
  "id": "0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "propertyId": "5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a",
  "isActive": true,
  "stage": "OfferRequested",
  "detailsDisclosure": "Unshared",
  "eTag": "\"0x8DCC…\""
}

GET /openapi/Offers/{id}/history: response

{
  "id": "1c2d3e4f-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
  "statusHistory": [
    { "from": "Finalizing", "to": "Sent", "at": "2026-09-24T14:00:00Z" },
    { "from": "Sent", "to": "Accepted", "at": "2026-09-25T09:30:00Z" }
  ]
}

Transactions and AI comping

Read-only. A transaction is one selling journey for a property; follow its stage here or, better, through the `transaction.stage_changed` webhook.

Examples

GET /openapi/Transactions/{transactionId}: response

{
  "id": "0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "propertyId": "5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a",
  "isActive": true,
  "stage": "CompingComplete",
  "detailsDisclosure": "Unshared",
  "eTag": "\"0x8DCB…\""
}

GET /openapi/Transactions/{transactionId}/comping: response

{
  "transactionId": "0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "propertyId": "5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a",
  "status": "completed",
  "revealUrl": "https://<app-host>/properties/5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a/offer-reveal"
}

Team

The people in your workspace and the teams they belong to. Use a member id as a property’s `agentId`.

Examples

GET /openapi/Team/members: response

{
  "items": [
    {
      "id": "3f6c1a2e-9b4d-4c1f-8a2b-1d2e3f4a5b6c",
      "fName": "Alex",
      "lName": "Smith",
      "email": "alex.smith@example.com",
      "role": "Member",
      "teamIds": ["d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a"],
      "archivedAt": null
    }
  ],
  "page": 1,
  "pageSize": 20,
  "totalCount": 1
}

POST /openapi/Team/members: request

{
  "email": "sam.lee@example.com",
  "teamIds": ["d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a"],
  "adminTeamIds": []
}

201 Created: response (an invite, not yet a member)

{
  "id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
  "token": "Kt7…",
  "expiresAt": "2026-10-08T12:00:00Z"
}

GET /openapi/Team/teams: response

{
  "items": [
    {
      "id": "d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a",
      "name": "Texas",
      "description": null,
      "parentTeamId": null,
      "memberCount": 12
    }
  ],
  "page": 1,
  "pageSize": 20,
  "totalCount": 1
}

Webhooks

Zoodealio calls your HTTPS endpoint at every step of the journey, so you never have to poll.

Delivery envelope (POST body)

{
  "id": "6f1e2d3c-4b5a-4968-8776-5a4b3c2d1e0f",
  "type": "comping.completed",
  "apiVersion": "2026-09-22",
  "occurredAt": "2026-09-22T18:04:11Z",
  "tenantId": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "data": { "…": "event-specific fields" }
}

Examples

POST /openapi/Webhooks: request

{
  "endpointUrl": "https://partner.example/zoodealio",
  "eventTypes": [
    "transaction.stage_changed",
    "property.files_imported",
    "comping.completed",
    "comping.failed",
    "offer.details_shared"
  ]
}

property.files_imported: data

{
  "importId": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
  "propertyId": "5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a",
  "photoIds": ["c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f", "…"],
  "documentIds": ["e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7a8b"],
  "failed": [
    {
      "source": "https://cdn.example.com/listings/102-shore/kitchen.jpg",
      "reason": "The URL answered 404."
    }
  ]
}

transaction.stage_changed: data

{
  "transactionId": "0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "propertyId": "5d4c3b2a-1f0e-9d8c-7b6a-5f4e3d2c1b0a",
  "previousStage": "CompingInProgress",
  "stage": "CompingComplete",
  "phase": "OffersReady",
  "outcome": null,
  "eTag": "\"0x8DCB…\""
}

Glossary

The terms these docs use, in one place.

API key
A personal zoo_… key. It acts as you, with your role and access, and is shown once. More
Property
The core record: an address, the home’s details, its homeowner, photos and documents. Everything else hangs off a property. More
Homeowner
The person selling. Attach one inline when you submit a property; the property returns their homeownerId. More
Estimated offer
The instant estimate a property gets on submission. The API returns only its range, estimatedRange: { low, high }. More
Actual offer
A real offer produced by AI comping. Its headline amount is visible; its terms appear once details are shared. More
AI comping
Zoodealio’s AI valuation that turns the estimate into real offers. Accepting the estimate starts it. More
Details shared
Terms stay null until you request details and Zoodealio shares them (detailsShared: true). More
Transaction
One selling journey for a property. Its stage says which step it is on; every change is a transaction.stage_changed webhook. More
Member
A person in your workspace: Owner, AccountAdmin or Member. A member id is a property’s agentId. More
Team
A group of members. Teams nest; a team admin reaches every team below theirs. More
Invite
How someone joins. Inviting returns an invite id; they become a member when they accept it. More
Import
A background download of a property’s photo and document URLs. It returns an importId; property.files_imported fires when it finishes. More
ETag / If-Match
Send the ETag from a GET as If-Match to reject a write if someone changed the record. More
Webhook
A signed POST Zoodealio sends to your HTTPS endpoint when something happens, retried until you answer 2xx. More

Questions