Skip to content

Data Share API Reference

All endpoints require a franchise-scoped bearer token. See Data Share API Authentication.

The franchise is always derived from the token, so no franchise_id parameter is accepted.

Rate Limits

Each API key is limited to 60 requests per minute and 5,000 requests per day. Every response includes the current limit state:

HeaderDescription
X-RateLimit-LimitPer-minute request limit
X-RateLimit-RemainingRequests remaining in the current minute
X-RateLimit-ResetUnix epoch (seconds) when the minute window resets
X-RateLimit-Limit-DayPer-day request limit
X-RateLimit-Remaining-DayRequests remaining in the current day

When a limit is exceeded the API responds with 429 Too Many Requests and a Retry-After header (seconds to wait). Clients should honor Retry-After and back off.

Pagination

The calls and contacts list endpoints use cursor (keyset) pagination. Each response returns a pagination object:

{ "page_size": 100, "has_more": true, "next_cursor": "eyJ0Ijoi..." }

To fetch the next page, pass the returned next_cursor value back as the cursor query parameter. When has_more is false, next_cursor is null and there are no more records. Cursors are opaque — do not parse or construct them. page_size accepts 1–200 (default 100).

GET https://api.breesy.app/data/locations

Returns the locations in your franchise so you can filter other endpoints by location_id.

Query Parameters

None.

Example Request

GET https://api.breesy.app/data/locations
Authorization: Bearer <api_token>

Example Response

{
"franchise_id": "0c8b2a1e-1111-2222-3333-444455556666",
"locations": [
{ "location_id": "aaaa1111-2222-3333-4444-555566667777", "location_name": "North Branch" },
{ "location_id": "bbbb1111-2222-3333-4444-555566667777", "location_name": "South Branch" }
]
}

GET https://api.breesy.app/data/calls

Returns voice-agent call data with Breesy recording links per call.

Query Parameters

NameRequiredDescription
location_idNoLimit to a single location UUID
start_dateNoInclusive start of the call timestamp range (ISO 8601 or YYYY-MM-DD)
end_dateNoInclusive end of the call timestamp range (ISO 8601 or YYYY-MM-DD)
call_idNoExact match on the call’s conversation_id
phoneNoMatch any of the caller ID, callback number, or customer phone (digits are normalized)
cursorNoOpaque pagination cursor from a previous response’s next_cursor
page_sizeNoRecords per page, 1–200 (default 100)
include_recording_urlNoSet to false to omit recording links (faster for bulk listing). Default true

Each call includes two stable, Breesy-branded links:

  • recording_url — a GET https://api.breesy.app/data/recordings/{conversation_id} endpoint that streams or downloads the recording through Breesy. Call it with your Authorization: Bearer <api_token> header. The response is the audio file directly (audio/mpeg); the upstream storage provider is never exposed. This link does not expire from your side — access is re-authorized on every request — so you can store it safely. See GET /data/recordings/{conversation_id} below.
  • recording_breesy_url — a permanent link that opens the call and its recording inside Breesy (sign-in required). Use this when a person should review the call.

Both fields may be null when no conversation_id is available, and are omitted entirely when include_recording_url=false.

Example Request

GET https://api.breesy.app/data/calls?start_date=2026-06-01&end_date=2026-06-23&page_size=50
Authorization: Bearer <api_token>

Example Response

{
"scope": { "franchise_id": "0c8b2a1e-1111-2222-3333-444455556666", "location_id": null },
"pagination": { "page_size": 50, "has_more": true, "next_cursor": "eyJ0IjoiMjAyNi0wNi0yMlQxODowNDoxMSswMDowMCIsImkiOiJDQTAxMjMuLi4ifQ" },
"calls": [
{
"conversation_id": "CA0123456789abcdef0123456789abcdef",
"caller_id": "+15551234567",
"timestamp": "2026-06-22T18:04:11+00:00",
"duration": "182",
"agent_type": "afterhours",
"call_category": "new_urgent_service_request",
"customer_name": "Jordan Smith",
"customer_email": "jordan@example.com",
"call_summary": "Water damage in the basement after a pipe burst.",
"loss_type": "Water",
"referral_source": "Google",
"notes": "Caller requested a morning callback.",
"property_area": "Basement",
"request_type": "INITIAL_SERVICE_REQUEST",
"service_location": "123 Main St, Springfield",
"callback_number": "+15557654321",
"location_id": "aaaa1111-2222-3333-4444-555566667777",
"location": "North Branch",
"transcript": "Agent: Thanks for calling...",
"real_estate": {
"address": "123 Main St, Springfield",
"last_sale_value": "415000",
"current_value": "468000",
"owner_name1": "Jordan Smith",
"owner_name2": null,
"room_count": "7",
"square_footage": "2100",
"year_built": "1998"
},
"recording_url": "https://api.breesy.app/data/recordings/CA0123456789abcdef0123456789abcdef",
"recording_breesy_url": "https://api.breesy.app/insights?tab=calldata&callId=conv_CA0123456789abcdef0123456789abcdef"
}
]
}

GET https://api.breesy.app/data/recordings/{conversation_id}

Streams or downloads a single call recording. The recording is resolved against your franchise on every request, so you can only fetch recordings for calls that belong to you.

Path / Query Parameters

NameRequiredDescription
conversation_idYesThe call’s conversation_id (path segment, or ?conversation_id= / ?call_id= query)

Behavior

  • Default — responds with 200 OK and streams the .mp3 through Breesy (Content-Type: audio/mpeg, Accept-Ranges: bytes). Use this URL in an <audio> element, a download link, or any HTTP client. The storage provider is never shown to the client.
  • Accept: application/json — returns metadata only (recording_url, breesy_url, content_type) without streaming audio. Useful when you want to confirm a recording exists before playing it.
  • This Breesy link itself is stable and does not expire; authorization is checked on every request.

Example Request

GET https://api.breesy.app/data/recordings/CA0123456789abcdef0123456789abcdef
Authorization: Bearer <api_token>

Example Metadata Response (Accept: application/json)

{
"conversation_id": "CA0123456789abcdef0123456789abcdef",
"recording_url": "https://api.breesy.app/data/recordings/CA0123456789abcdef0123456789abcdef",
"breesy_url": "https://api.breesy.app/insights?tab=calldata&callId=conv_CA0123456789abcdef0123456789abcdef",
"content_type": "audio/mpeg"
}

Returns 404 Recording not found when the call does not exist or belongs to another franchise.

GET https://api.breesy.app/data/contacts

Reads contacts from your CRM. Returns a paginated list, or a single contact when id is supplied.

Query Parameters

NameRequiredDescription
idNoReturn a single contact by its numeric ID
phoneNoMatch the primary or alternate phone (digits are normalized)
searchNoCase-insensitive match on name, organization, email, or phone
location_idNoLimit to contacts tracked at a location UUID
cursorNoOpaque pagination cursor from a previous response’s next_cursor
page_sizeNoRecords per page, 1–200 (default 100)

Example Request

GET https://api.breesy.app/data/contacts?search=jordan&page_size=25
Authorization: Bearer <api_token>

Example Response

{
"scope": { "franchise_id": "0c8b2a1e-1111-2222-3333-444455556666" },
"pagination": { "page_size": 25, "has_more": false, "next_cursor": null },
"contacts": [
{
"id": 4821,
"first_name": "Jordan",
"last_name": "Smith",
"organization_name": null,
"is_business": false,
"primary_phone": "+15551234567",
"primary_email": "jordan@example.com",
"alternate_phones": [],
"alternate_emails": [],
"tags": ["VIP"],
"primary_contact_tags": [],
"notes": "Prefers morning calls.",
"referred_by": "Google",
"address": {
"line1": "123 Main St",
"line2": null,
"city": "Springfield",
"state": "IL",
"postal": "62704",
"country": "US"
},
"location_id": ["aaaa1111-2222-3333-4444-555566667777"],
"total_calls": 3,
"last_contact_date": "2026-06-22T18:04:11+00:00",
"lifetime_value": 12500.0,
"is_verified": true,
"created_at": "2026-01-10T12:00:00+00:00",
"updated_at": "2026-06-22T18:10:00+00:00"
}
]
}

PATCH https://api.breesy.app/data/contacts

Updates an existing contact. Only the fields you include are changed.

Request Body

FieldRequiredDescription
idYesThe numeric contact ID to update
first_nameNo
last_nameNo
organization_nameNo
is_businessNoBoolean
primary_phoneNoNormalized to E.164 on save; must be a valid phone
primary_emailNo
alternate_phonesNoArray of phone strings
alternate_emailsNoArray of email strings
tagsNoArray of tag strings
primary_contact_tagsNoArray of tag strings
notesNo
referred_byNo
addr_line1, addr_line2, addr_city, addr_state, addr_postal, addr_countryNoAddress fields
location_idNoArray of location UUID strings

Example Request

PATCH https://api.breesy.app/data/contacts
Authorization: Bearer <api_token>
Content-Type: application/json
{
"id": 4821,
"tags": ["VIP", "Repeat Customer"],
"notes": "Signed annual maintenance agreement."
}

Example Response

{
"contact": {
"id": 4821,
"first_name": "Jordan",
"last_name": "Smith",
"tags": ["VIP", "Repeat Customer"],
"notes": "Signed annual maintenance agreement."
}
}

POST https://api.breesy.app/data/contacts

Creates a contact. Breesy matches on phone number to avoid duplicates; if a matching contact already exists, that contact is updated with the supplied fields and matched_existing is true.

Request Body

primary_phone is required. All other writable fields are optional.

Example Request

POST https://api.breesy.app/data/contacts
Authorization: Bearer <api_token>
Content-Type: application/json
{
"first_name": "Alex",
"last_name": "Doe",
"primary_phone": "555-987-6543",
"primary_email": "alex@example.com",
"tags": ["New Lead"]
}

Example Response

{
"contact": {
"id": 5099,
"first_name": "Alex",
"last_name": "Doe",
"primary_phone": "+15559876543",
"primary_email": "alex@example.com",
"tags": ["New Lead"]
},
"matched_existing": false
}

Expected Errors

StatusMeaning
400Missing required field, invalid JSON, invalid primary_phone, or invalid cursor
401Missing, invalid, expired, or revoked bearer token
404The contact id does not exist within this franchise (PATCH)
429Rate limit exceeded — honor the Retry-After header and retry later