Overview
Get started
Create an API key, then ask Phil your first question over HTTPS. Keys belong to your workspace and every request is tracked on the Usage page.
Quickstart
Base URL:
Playground
Runs the same request the API runs, and it counts on the Usage page like any API call.
API keys
A key is shown once, at creation. Store it server side and never in a browser or repository.
| Name | Key | Created | Last used | Status |
|---|
No keys yet. Create your first key to call the API.
Usage
Requests per day
By model
| Model | Requests | Input tokens | Output tokens |
|---|
By credential
| Credential | Requests | Input tokens | Output tokens | Last used |
|---|
Limits
Limits apply per key. During the beta the API is included with the Agent plan, with no separate API billing.
| Limit | Value | What happens at the limit |
|---|---|---|
| Full Phil requests per hour (/chat) | 100 | The API answers 429. Wait for the next hour window. |
| Document requests per hour (/ask) | 300 | The API answers 429. Wait for the next hour window. |
| Concurrent requests | 4 | The API answers 429. Finish an in-flight request first. |
| Active keys per workspace | 10 | Key creation is refused until one is revoked. |
| Request body | 16 KB | The API answers 413. |
| Answer deadline | 90 seconds | The request fails and does not count against your answer. |
API overview
The Phil API is a RESTful API that gives your systems programmatic access to Phil, RealestateCopilot's property AI. Everything the API can do, how to authenticate, and what every endpoint does is on this page.
Base URL
All endpoints live under one base URL. Requests are HTTPS only.
Prerequisites
To use the Phil API you need:
| A RealestateCopilot account on the Agent plan | Accounts are created on the main site; the API is included with the Agent plan during the beta, with no separate API billing. |
| An API key | Created on the API keys page of this console. The full key is shown once, at creation; store it server side, never in a browser, app, or repository. |
Available APIs
Four endpoints, two kinds of work. Chat is Phil himself; the other three are strict, deterministic building blocks.
| API | Endpoint | What it does |
|---|---|---|
| Chat | POST /chat | Full Phil. He decides for himself whether to search live listings, run web research on verified Australian sources, pull suburb market data, or read your documents on the way to his reply. Conversational and stateless: you pass the history each call. |
| Ask | POST /ask | Answers strictly from your workspace documents, with a citation for every claim. When the documents do not cover the question it abstains and says so, rather than guessing. It never searches the web and never uses knowledge outside your files. |
| Documents | GET /documents | Lists the documents your key can see, cursor paginated. Use it to discover what Ask can draw on. |
| Health | GET /health | Your workspace's API state: which capabilities are granted, connector and sync status, and how many sources exist. Good for monitoring and for a first "is my key working" call. |
Rule of thumb: "what does this file say" is Ask; "help me think, find, or advise" is Chat.
Models
Chat comes in two strengths. Pick per request with the optional model field; every response names the model that served it.
| Model | Built for | Choose it when |
|---|---|---|
phil-maxDefault |
The most capable Phil, the same brain the Agent plan runs on the website. Deepest reasoning, strongest at complex agentic work: multi-step advisory, campaign strategy, weighing evidence across sources. | Quality matters more than cost. Client-facing answers, advisory work, anything a person will act on. |
phil |
The fast, efficient Phil. Sharp on focused questions and document work, at a fraction of the compute. | High-volume pipelines, quick lookups, summaries, and processing where throughput and cost win. |
phil-max uses substantially more compute per request; when API billing starts it will be priced accordingly, and the Usage page already splits your consumption per model so you can see the trade-off today. Model names are RealestateCopilot's own and stay stable regardless of the technology underneath.
Authentication
Every request carries your key as a bearer token. There are no other authentication modes.
| Header | Value | Required |
|---|---|---|
Authorization | Bearer rec_agent_... | Yes, on every request |
Content-Type | application/json | Yes, on POST requests |
The API is strictly server to server. Any request carrying a browser artefact, a Cookie or an Origin header, is refused with 403 before authentication, so a session stolen from a browser is worthless here and a leaked key cannot be driven from a web page. Key failures of every kind (unknown, wrong, revoked, expired) return one identical, deliberately uninformative 401.
Getting and managing keys
Keys are created on the API keys page: name the key, copy it from the one-time reveal, done. A workspace can hold 10 active keys. Rotate swaps a key for a fresh secret with the same access; Revoke kills it immediately. Every key's calls are tracked separately on the Usage page.
Requests and responses
Size and time limits
| Limit | Value |
|---|---|
| Request body, all endpoints | 16 KB (413 above it) |
| Chat message length | 4000 characters |
| Chat history | 12 turns, each up to 4000 characters |
| Answer deadline | 90 seconds for Ask, 180 for Chat; a timed-out request fails cleanly and is safe to retry |
Common response fields
| Field | Meaning |
|---|---|
requestId | Unique id for the HTTP request. Quote it when reporting a problem. |
agentRequestId | The id of the admitted unit of work (areq_...), the one you will find on the Usage page. |
usage | inputTokens and outputTokens actually spent producing the answer, summed across every internal model call. |
Idempotency
Every POST requires a clientRequestId, a unique string you choose. It makes retries safe: sending the identical request again returns "replay": true without doing the work twice, and reusing an id with different input is refused with 409. On a 502, retry with the same id.
Pagination
List endpoints take a limit query parameter (up to 200) and return a nextCursor field. Pass it back as ?cursor= to fetch the next page; a null nextCursor means you have everything. Cursors stay stable while new documents arrive.
Rate limits and usage
Limits apply per key and protect the platform; they are not billing. Hitting one returns 429 with nothing consumed.
| Limit | Value |
|---|---|
| Chat requests | 100 per hour per key |
| Ask requests | 300 per hour per key |
| Concurrent requests | 4 per key |
Every successful call is recorded with its token usage and appears on the Usage page within seconds, grouped by key, so what you see there is exactly what your systems spent. Playground runs count the same way, under their own "Playground" row.
Endpoint reference
Copy-ready requests and response shapes for each endpoint.
POST /agent/v1/chat
Full Phil: he decides for himself whether to search live listings, run web research on Australian sources, pull suburb data, or read your documents on the way to his reply. Stateless: pass your own conversation history each call.
Body
| Field | Type | Notes |
|---|---|---|
message | string | Required. Up to 4000 characters. |
clientRequestId | string | Required. Your unique id for this request; retries with the same id never run the work twice. |
history | array | Optional. Up to 12 turns of {"role": "user" | "assistant", "content": "..."}. |
model | string | Optional. phil-max (default) or phil. See Models above. An unknown name is refused with 400 invalid_model. |
Response
{
"reply": "...",
"intent": "SEARCH",
"listings": 6,
"model": "phil-max",
"usage": { "inputTokens": 9120, "outputTokens": 640, "model": "phil-max" },
"agentRequestId": "areq_..."
}
POST /agent/v1/ask
Ask Phil a question grounded in your workspace documents. Phil answers with citations, or abstains when the documents do not cover it.
Body
| Field | Type | Notes |
|---|---|---|
question | string | Required. The question to answer. |
clientRequestId | string | Required. Your unique id for this request. Retrying with the same id never runs the work twice. |
collectionIds | string[] | Optional. Restrict the answer to these sources. |
Response
{
"answer": "...",
"abstained": false,
"citations": [{ "documentId": "...", "title": "...", "evidenceId": "..." }],
"usage": { "inputTokens": 812, "outputTokens": 214 },
"agentRequestId": "areq_..."
}
GET /agent/v1/documents
List the documents your key can see. Cursor paginated.
GET /agent/v1/health
Your workspace's API health: active grants, connector state, and source counts.
Errors
| Status | Meaning |
|---|---|
| 401 | The key is missing, wrong, revoked, or expired. |
| 403 | The key or workspace is not allowed to do this. |
| 409 | The same clientRequestId was reused with a different question. |
| 413 | The request body is over 16 KB. |
| 429 | Rate limited. Slow down and retry later. |
| 502 | Phil could not produce an answer. Safe to retry with the same clientRequestId. |
Errors
The API fails in a small, predictable vocabulary. Every error is JSON with a stable machine-readable code and a request id; there are no stack traces, no provider error strings, and no error that names another workspace's data.
Error shape
Every error response has the same two fields. POST failures that happened after admission also carry the agentRequestId of the failed unit of work.
{
"error": "rate_limited",
"requestId": "req_018eeWyXxfu5pfWk"
}
HTTP status codes
| Status | Codes | Meaning and what to do |
|---|---|---|
| 400 | message_required, question_required, client_request_id_required, message_too_long, body_not_json, content_type_must_be_json, invalid_model, invalid_beta_header | The request itself is wrong. Fix the body or headers; retrying unchanged will fail identically. |
| 401 | authentication_failed | The key is missing, malformed, wrong, revoked, or expired. All key failures return this one identical error on purpose, so a caller can never learn which part was wrong. Check the key on the API keys page. |
| 403 | denied, browser_requests_not_accepted | denied: the key or workspace lacks the capability; the reason is deliberately not disclosed. browser_requests_not_accepted: the request carried a Cookie or Origin header; the API is server to server only. |
| 404 | not_found | No such endpoint, or the API is switched off for this server. Check the path against the API overview. |
| 409 | client_request_id_conflict | A clientRequestId was reused with different input. Choose a fresh id for new work; ids are bound to their exact request forever. |
| 413 | body_too_large | The request body is over 16 KB. Shorten the message or history. |
| 429 | rate_limited | The per-key ceiling was hit: 100 chat or 300 ask requests in the hour window, or more than 4 requests in flight at once. Nothing was consumed. Back off and retry after the window turns. |
| 502 | reply_unavailable, answer_unavailable | Phil could not produce this answer. Safe to retry WITH THE SAME clientRequestId: idempotency guarantees the retry can never double-run or double-count completed work. |
| 503 | evidence_unavailable, admission_unavailable | A platform component is briefly unavailable. Retry with backoff; if it persists for minutes, report it with the request id. |
Request ids
Every response carries a requestId (the HTTP request) and successful POSTs also carry an agentRequestId (the admitted unit of work, the id you will find on the Usage page). Quote both when reporting a problem; they let us find the exact request in the audit trail in seconds.
Retry guidance
| You got | Do this |
|---|---|
| 429 | Wait and retry after the hour window turns, or space requests out. Do not tight-loop; the window does not reset faster under pressure. |
| 502 | Retry immediately with the same clientRequestId, then with backoff. If the original actually completed, the retry returns "replay": true instead of re-running. |
| 503 | Retry with exponential backoff starting around 2 seconds. |
| Network timeout | Chat turns can run up to 180 seconds when Phil searches or researches; set client timeouts to at least 190 seconds, then treat a timeout like a 502. |
The API does not stream yet, so keep the connection open for the full turn. Anything you receive outside this page's vocabulary is a bug: report it with the request id.
Beta
Where the platform is in its life, what that means for you, and how experimental features will be introduced without surprising your integration.
The platform is in beta
The whole Phil API is currently beta, included with the Agent plan with no separate API billing. While the beta runs:
| Shapes may grow | Responses can gain NEW fields at any time without notice. Build readers that ignore fields they do not recognise. |
| Breaking changes get a new path | Existing fields and semantics under /agent/v1 do not change meaning. A breaking change ships as /agent/v2 and v1 keeps working through a notice period. |
| Limits can move | Rate ceilings may be tuned during the beta; the Limits page always shows the current values. |
| The error vocabulary can grow | New codes may appear within the documented statuses. Branch on status first, code second, and default sensibly. |
The rec-beta header
Experimental features will be opt-in before they join the stable surface. To use one, send its documented name in the rec-beta header:
curl https://console.realestatecopilot.com.au/agent/v1/chat \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "rec-beta: FEATURE_NAME" \
-H "Content-Type: application/json" \
-d '{ "message": "...", "clientRequestId": "..." }'
Several features combine with commas: rec-beta: feature-one,feature-two. Feature names follow feature-name-YYYY-MM-DD, dated by their beta release, and each feature's documentation states the exact name to send.
No beta features are open right now. The mechanism is live ahead of its first feature, so integrations can rely on its behaviour today: an unknown or misspelled name is refused with 400 invalid_beta_header rather than being silently ignored, which means a typo can never make you believe a feature was on when it was not.
{
"error": "invalid_beta_header",
"requestId": "req_018eeWyXxfu5pfWk"
}
What beta features may do
A feature behind a beta name is experimental and may change shape with notice, be withdrawn, carry its own rate limits, or graduate into the stable surface (at which point the header becomes unnecessary and is ignored for that name). Anything NOT behind a beta name follows the stability rules above.