Skip to content

Recipe: Client, lender, or lead database ​

Nightly (or hourly) sync from Salesforce, HubSpot, a LOS, or your own client table into Appraiser Flow. The office keeps working in Clients and CRM. Your system remains the source of the external id.

1. Key ​

Settings → Public API → Create API Key, environment Test.

You are loadingScopes
Companies onlyorganizations:read, organizations:write
Companies and leadsthose, plus leads:read, leads:write
Status after the office opens an orderorders:read

Store AF_API and AF_API_KEY on the worker, not in the CRM’s client-side automation.

2. Map columns ​

Your columnAppraiser Flow fieldNotes
Account or company idexternal_referenceRequired for safe updates. Keep it stable.
System nameexternal_sourceExample: salesforce, hubspot, encompass
Company namenameExact name is the last-resort match if the id is new
Lender vs AMC vs otherclient_typelender, amc, private_party, or other
Main phonephone10-digit US. Stored as (615) 555-0100
Main emailemail
WebsitewebsiteRequired for every type except lender
Street, city, state, postaladdress_line1, city, state, postal_codeRequired for every type except lender. state is two letters
Your account numberaccount_numberUsed as a match when external_reference is new
Person idcontacts[].external_reference
Person name, email, phonecontacts[].display_name, email, phone
Rolecontacts[].contact_typeorder_assigner, account_manager, billing, or other
Deal idlead external_reference
Deal namelead title
Pipeline stagelead stagelead, qualified, proposal, won, lost
Expected fee in centslead amount_centsEngagement fee, not a property value

Prefix ids if two systems could collide: salesforce:001xx, hubspot:8841.

3. Upsert companies, then leads ​

Send companies first so a lead can use organization_external_reference. A lead may instead include a nested organization, which needs organizations:write and creates the company in the same call.

Batch up to 100 companies per request. One bad business row returns action: "error" and the other rows still save. An invalid phone or unknown client_type fails the whole request before any row is written, so validate those before you POST.

python
import os
import uuid
import requests

API = os.environ["AF_API"].rstrip("/") + "/public/v1"
HEADERS = {
    "Authorization": f"Bearer {os.environ['AF_API_KEY']}",
    "Content-Type": "application/json",
}

def upsert_companies(rows: list[dict]) -> None:
    response = requests.post(
        f"{API}/organizations/batch",
        headers={**HEADERS, "Idempotency-Key": uuid.uuid4().hex},
        json={"items": rows},
        timeout=60,
    )
    response.raise_for_status()
    for item in response.json()["results"]:
        if item["action"] == "error":
            print(item["external_reference"], item["error_code"], item["message"])

def upsert_lead(deal: dict) -> None:
    response = requests.post(
        f"{API}/leads",
        headers={**HEADERS, "Idempotency-Key": uuid.uuid4().hex},
        json=deal,
        timeout=30,
    )
    response.raise_for_status()

Use a new idempotency key for each batch attempt with a new body. Keep external_reference stable across nights. Reusing one idempotency key with a changed body returns 409.

4. Check the office ​

Open Clients and the company you sent. External Reference should match the source id. Open CRM for the lead’s stage.

Poll an existing order when the lender portal needs status:

http
GET /public/v1/orders?loan_number=LN-4421
Authorization: Bearer af_test_…

That call needs orders:read. It returns status, due date, engagement fee, and address. It does not return internal notes or an appraisal value.

5. Promote ​

Create a Live key with the same scopes, point the worker at https://api.appraiserflow.ai, deploy, then revoke the test key if staging should stop.

API base for Try-it: https://staging-api.appraiserflow.ai