Reporting API

A scoped API for pulling your office's EvaCore data into Power BI, Excel or your ERP. Every key belongs to one office, reads only that office's data, and only within the scopes the owner chose.

1. Create a key

Only the office owner creates keys: Integrations → Reporting API → New key. Choose a name, the scopes and an expiry. The key (it starts with evak_) is shown once. Store it like a password. Revoke it any time and it stops at once.

ScopePermissionEndpoints
readRead all resources (including messages)/api/v1/leads, /api/v1/deals, /api/v1/properties, /api/v1/projects, /api/v1/units, /api/v1/reservations, /api/v1/activities, /api/v1/tasks, /api/v1/agents, /api/v1/teams, /api/v1/conversations, /api/v1/messages
leads:writeCreate, update and assign leads/api/v1/leads
properties:writeCreate and update properties and status/api/v1/properties
leads:readLeads/api/v1/leads
deals:readDeals/api/v1/deals
properties:readProperties/api/v1/properties
projects:readProjects, units and reservations/api/v1/projects, /api/v1/units, /api/v1/reservations
activities:readActivities and tasks/api/v1/activities, /api/v1/tasks
agents:readAgents and teams/api/v1/agents, /api/v1/teams
conversations:readConversations (metadata only)/api/v1/conversations
messages:readMessage text/api/v1/messages

2. Send the key

In the Authorization header, either way. Never put the key in the URL.

curl -H "Authorization: Bearer evak_YOUR_KEY" \
  "https://app.evacore.io/api/v1/leads?limit=100"

# Basic auth. Any user name, the key as the password (what Power BI uses):
curl -u "api:evak_YOUR_KEY" "https://app.evacore.io/api/v1/deals?updated_since=2026-10-01T00:00:00Z"

# CSV for Excel / Power BI. Every matching row in one response:
curl -u "api:evak_YOUR_KEY" "https://app.evacore.io/api/v1/properties?format=csv&fields=title,city,price,status" -o properties.csv

3. The response

UTF-8 JSON with Arabic text as-is. Times are UTC ISO 8601; SAR amounts are strings with two decimals ("1500000.00") so no precision is lost. An empty field is null.

{
  "object": "list",
  "resource": "leads",
  "data": [
    {
      "id": "0e7b6a52-3c55-4a8e-b0a4-7c4d1f2e9a31",
      "name": "محمد العتيبي",
      "phone": "+966501234567",
      "status": "جديد",
      "budget": "1500000.00",
      "created_at": "2026-10-01T09:15:01.902Z",
      "updated_at": "2026-10-03T12:40:22.118Z"
    }
  ],
  "has_more": true,
  "next_cursor": "WyIyMDI2LTEwLTAzVDEyOjQwOjIyLjExODQ1MVoiLCIwZTdi…",
  "next": "/api/v1/leads?limit=100&cursor=WyIyMDI2…"
}

A page has 100 rows by default and up to 1000 with limit. Pick columns with fields (comma-separated; id is always included). format=csv returns every row in one response, field names on the first line.

4. Incremental sync

  1. Rows come oldest change first. Fetch the first page, then pass next_cursor as cursor until has_more is false.
  2. Keep the last next_cursor. Next sync, send it and you get only what was created or changed after it. Every row comes whole, so upsert by id.
  3. Or use updated_since with your last sync time minus 5 minutes (to cover changes that commit late) and upsert by id.
  4. Deleted rows are not reported incrementally: do a full pull now and then (weekly, say).

5. Connect Power BI

  1. Power BI Desktop → Get data → Web.
  2. Paste a table's CSV URL, e.g. https://app.evacore.io/api/v1/leads?format=csv
  3. In the sign-in dialog choose Basic: any user name (e.g. api) and the key as the password. Apply it to https://app.evacore.io so every table uses the key.
  4. If the Arabic looks garbled: in the preview set File origin = 65001: Unicode (UTF-8).
  5. Repeat Web for each table you need (deals, properties, projects, units, reservations, activities, tasks, agents, teams) and relate them on the *_id fields.
  6. After publishing to the Power BI Service: Settings → Data source credentials → Basic with the same key, then turn on Scheduled refresh.

Excel works the same way: Data → From Web → the same URL → Basic.

Writes and retries

Use POST /api/v1/leads or POST /api/v1/properties to create; PATCH the same path with /{id} to update. Requires leads:write or properties:write; read never grants writes. Update a lead's status or assign a sales user in the same office, or update a property's details and status. See OpenAPI for allowed fields and values.

Send JSON with a unique Idempotency-Key per operation (1 to 128 printable ASCII characters). Retrying the same request and key returns the original response without repeating the mutation. A changed body or path returns 409. Retry 503 with the same key. Project-linked property status uses the reservation workflow; resolve active shared lead assignments in the office first.

Owners see daily counts per key for the last 30 Riyadh days on the API keys page. Every authenticated read, write, replay or refusal counts once. All share a 120/minute budget; CSV costs 10 budget units and counts as one request.

6. Limits and errors

StatusMeaning
400Invalid parameter (unknown field, date, cursor or limit).
401Missing, unknown, revoked or expired key.
403The key lacks this table's scope.
404Unknown table.
429Over 120 requests a minute for this key (a CSV export counts as 10). Wait the seconds in Retry-After.

Every error has the shape: { "error": { "code": "…", "message": "…" } }. Writes require a separate scope and an Idempotency-Key.

7. Tables and fields

The full OpenAPI 3.1 specification: https://app.evacore.io/api/v1/openapi.json

GET /api/v1/leads

Leads (customers) of the office, every channel. Scope: leads:read. updated_since: rows created or changed since the given time.

FieldTypeMeaning
idid (UUID)Lead id.
namestringName.
phonestringPhone, international format when known.
emailstringEmail.
statusstringPipeline status, as shown in the dashboard.
sourcestringWhere the lead came from (whatsapp, bayut, website…).
channelstringConversation channel.
intentstringBuy / rent intent.
scorestringLead score.
property_typestringWanted property type.
citystringWanted city.
districtstringWanted district.
budgetSAR amount (string, 2 decimals)Budget, SAR.
budget_minSAR amount (string, 2 decimals)Budget lower bound, SAR.
budget_maxSAR amount (string, 2 decimals)Budget upper bound, SAR.
bedroomsintegerWanted bedrooms.
timelinestringPurchase timeline.
financingstringFinancing (cash / bank).
tagslist of stringsTags.
agent_idstringAssigned agent (agents.id).
team_idid (UUID)Team (teams.id).
project_idid (UUID)Project (projects.id).
merged_into_lead_idid (UUID)Set when this lead was merged into another.
opted_outbooleanAsked not to be contacted.
follow_up_atUTC date-time (ISO 8601)Next follow-up (UTC).
first_contact_atUTC date-time (ISO 8601)First contact sent (UTC).
created_atUTC date-time (ISO 8601)When it was created (UTC).
updated_atUTC date-time (ISO 8601)When it last changed (UTC). What updated_since compares.

GET /api/v1/deals

Deals (sale / rent opportunities) on leads. Scope: deals:read. updated_since: rows created or changed since the given time.

FieldTypeMeaning
idid (UUID)Deal id.
lead_idid (UUID)Lead (leads.id).
kindstringDeal kind.
titlestringTitle.
stagestringStage.
valueSAR amount (string, 2 decimals)Value, SAR.
property_idid (UUID)Property (properties.id).
unit_idid (UUID)Project unit (units.id).
agent_idstringAgent (agents.id).
expected_close_datedate (YYYY-MM-DD)Expected close date.
won_atUTC date-time (ISO 8601)Won (UTC).
lost_atUTC date-time (ISO 8601)Lost (UTC).
lost_reasonstringLost reason.
created_atUTC date-time (ISO 8601)When it was created (UTC).
updated_atUTC date-time (ISO 8601)When it last changed (UTC). What updated_since compares.

GET /api/v1/properties

Listings of the office. Scope: properties:read. updated_since: rows created or changed since the given time.

FieldTypeMeaning
idid (UUID)Property id.
titlestringTitle.
descriptionstringPublic description.
typestringType (فيلا، شقة، أرض…).
purposestringللبيع / للإيجار.
statusstringStatus (متاح، مباع، مؤجر، مؤرشف…).
citystringCity.
districtstringDistrict.
priceSAR amount (string, 2 decimals)Price, SAR.
rent_monthlySAR amount (string, 2 decimals)Monthly rent, SAR.
area_m2numberArea, m².
built_up_area_m2numberBuilt-up area, m².
bedroomsintegerBedrooms.
bathroomsintegerBathrooms.
age_yearsintegerAge, years.
project_idid (UUID)Project (projects.id).
agent_idslist of stringsAgents (agents.id).
ad_license_numberstringREGA ad licence number.
external_sourcestringImported from (aqar, bayut…).
external_refstringId in that source.
public_linkstringPublic page.
created_atUTC date-time (ISO 8601)When it was created (UTC).
updated_atUTC date-time (ISO 8601)When it last changed (UTC). What updated_since compares.

GET /api/v1/projects

Off-plan / development projects. Scope: projects:read. updated_since: rows created or changed since the given time.

FieldTypeMeaning
idid (UUID)Project id.
namestringName.
developerstringDeveloper.
citystringCity.
districtstringDistrict.
statusstringStatus.
delivery_datedate (YYYY-MM-DD)Delivery date.
wafi_numberstringWafi licence.
price_minSAR amount (string, 2 decimals)Lowest price, SAR.
price_maxSAR amount (string, 2 decimals)Highest price, SAR.
descriptionstringDescription.
created_atUTC date-time (ISO 8601)When it was created (UTC).
updated_atUTC date-time (ISO 8601)When it last changed (UTC). What updated_since compares.

GET /api/v1/units

Units of every project. Scope: projects:read. updated_since: rows created or changed since the given time.

FieldTypeMeaning
idid (UUID)Unit id.
project_idid (UUID)Project (projects.id).
property_idid (UUID)Linked listing (properties.id).
codestringUnit code.
buildingstringBuilding.
floorintegerFloor.
modelstringModel.
area_m2numberArea, m².
built_up_area_m2numberBuilt-up area, m².
bedroomsintegerBedrooms.
bathroomsintegerBathrooms.
priceSAR amount (string, 2 decimals)Price, SAR.
statusstringStatus (available, held, reserved, sold…).
created_atUTC date-time (ISO 8601)When it was created (UTC).
updated_atUTC date-time (ISO 8601)When it last changed (UTC). What updated_since compares.

GET /api/v1/reservations

Unit reservations and deposits. Scope: projects:read. updated_since: rows created or changed since the given time.

FieldTypeMeaning
idid (UUID)Reservation id.
project_idid (UUID)Project (projects.id).
unit_idid (UUID)Unit (units.id).
lead_idid (UUID)Lead (leads.id).
statusstringStatus (held, paid, sold, released, expired, cancelled).
depositSAR amount (string, 2 decimals)Deposit, SAR.
payment_modestringPayment mode.
hold_expires_atUTC date-time (ISO 8601)Hold deadline (UTC).
paid_atUTC date-time (ISO 8601)Deposit confirmed (UTC).
created_bystringCreated by (agents.id).
closed_bystringClosed by (agents.id).
created_atUTC date-time (ISO 8601)When it was created (UTC).
updated_atUTC date-time (ISO 8601)When it last changed (UTC). What updated_since compares.

GET /api/v1/activities

Calls / call-centre outcomes and lead status changes. Scope: activities:read. updated_since: rows created since (rows never change) the given time.

FieldTypeMeaning
idstringActivity id (call:… or status:…).
typestringcall or status_change.
lead_idid (UUID)Lead (leads.id).
user_idstringWho did it (agents.id).
actionstringAction.
outcomestringCall outcome.
from_statusstringPrevious status.
to_statusstringNew status.
follow_up_atUTC date-time (ISO 8601)Follow-up set (UTC).
created_atUTC date-time (ISO 8601)When it was created (UTC).

GET /api/v1/tasks

Office tasks. Scope: activities:read. updated_since: rows created or changed since the given time.

FieldTypeMeaning
idid (UUID)Task id.
titlestringTitle.
statusstringStatus.
due_atUTC date-time (ISO 8601)Due (UTC).
lead_idid (UUID)Lead (leads.id).
assignee_idstringAssignee (agents.id).
created_bystringCreated by (agents.id).
created_atUTC date-time (ISO 8601)When it was created (UTC).
updated_atUTC date-time (ISO 8601)When it last changed (UTC). What updated_since compares.

GET /api/v1/agents

People of the office. Scope: agents:read. updated_since: rows created or changed since the given time.

FieldTypeMeaning
idstringAgent id: what agent_id fields refer to.
namestringName.
emailstringEmail.
rolestringRole (org:admin = owner, org:member…).
team_idslist of stringsTeams (teams.id).
created_atUTC date-time (ISO 8601)When it was created (UTC).
updated_atUTC date-time (ISO 8601)When it last changed (UTC). What updated_since compares.

GET /api/v1/teams

Sales teams. Scope: agents:read. updated_since: rows created or changed since the given time.

FieldTypeMeaning
idid (UUID)Team id.
namestringName.
region_idid (UUID)Region.
leader_idstringLeader (agents.id).
member_idslist of stringsMembers (agents.id).
created_atUTC date-time (ISO 8601)When it was created (UTC).
updated_atUTC date-time (ISO 8601)When it last changed (UTC). What updated_since compares.

GET /api/v1/conversations

WhatsApp, Instagram and TikTok conversations. Counts and times only, never message text. Scope: conversations:read. updated_since: rows with a message since the given time.

FieldTypeMeaning
idstringConversation id (channel:contact).
channelstringwhatsapp, instagram or tiktok.
contactstringCustomer phone or handle.
contact_namestringCustomer name as shown.
lead_idstringLead (leads.id).
message_countintegerMessages.
inbound_countintegerFrom the customer.
outbound_countintegerFrom the office or Eva.
first_message_atUTC date-time (ISO 8601)First message (UTC).
last_message_atUTC date-time (ISO 8601)Last message (UTC): what updated_since compares.

GET /api/v1/messages

Message text of every conversation. A deleted message keeps its row with text null. Scope: messages:read. updated_since: rows created or changed since the given time.

FieldTypeMeaning
idstringMessage id.
conversation_idstringConversation (conversations.id).
channelstringwhatsapp, instagram or tiktok.
directionstringinbound (customer) or outbound.
senderstringWho sent it (customer, eva, agent…).
sent_by_user_idstringAgent who sent it (agents.id).
textstringMessage text, Arabic intact.
media_typestringMedia type, if any.
lead_idstringLead (leads.id).
created_atUTC date-time (ISO 8601)Sent (UTC).
updated_atUTC date-time (ISO 8601)When it last changed (UTC). What updated_since compares.
Reporting API for developers · EvaCore