GET /v1/openapi.json
This specification as JSON
The OpenAPI 3.1 document for this API. No authentication.
Returns
200 The OpenAPI document.
curl "https://api.hifoyer.com/v1/openapi.json"Foyer API
Every endpoint of the Foyer API, generated from the OpenAPI 3.1 specification that the API itself serves at https://api.hifoyer.com/v1/openapi.json. Load that file into Postman, an SDK generator or your AI agent to get all 78 operations with their exact types.
Foyer is an AI phone call screener. A member forwards their phone line to Foyer; Foyer answers callers it does not know, asks who they are and why they are calling, and then rings the member, holds the call for review, takes a voicemail, or turns the caller away. Saved contacts ring straight through.
This API lets a member connect their own AI assistant or scripts to their own Foyer account. Everything it returns belongs to the member who made the key; there is no way to reach another member's data.
Send a personal API key in the Authorization header: Authorization: Bearer fyr_…. The member makes keys in the Foyer app or dashboard under Settings › Connected apps. A key is shown once, expires after 90 days (or sooner if chosen), and can be revoked at any time. Keys are never accepted in the URL.
Each key carries the scopes the member ticked when making it:
| Scope | What it allows |
|---|---|
calls:read | See calls, summaries, voicemails list, the review queue, statistics and search. |
transcripts:read | Read the words: the screening conversation, voicemail text and the recorded call's transcript. Also lets search look inside them. |
audio:read | Get short-lived links to recordings (voicemail, screening, the whole call). Off unless the member grants it. |
contacts:read | See contacts and how Foyer treats each caller. |
settings:read | See settings, numbers and forwarding lines, notes and what the member's host (the AI screener) knows. |
calls:write | Mark calls read or unread, archive, refile and report spam. |
contacts:write | Add contacts and change how Foyer treats a caller. |
settings:write | Change any setting the member can change in the app. |
notes:write | Add or remove notes for the host and manage the facts it knows. |
A request without the scope it needs gets 403 insufficient_scope.
Every Foyer plan can read. Changing anything (any *:write scope) needs Concierge or Chief of Staff; the free period and complimentary access count as the top tier. On Doorman, write requests get 403 plan_does_not_include_writes and nothing is changed; a key made with write scopes keeps them and they work again after an upgrade. Use GET /v1/me to see what the current key may do.
Per member: 60 requests a minute and 10,000 a day, and at most 20 searches a minute. Over a limit the API answers 429 rate_limited with a Retry-After header (seconds).
Lists are newest first. A page has data and next_cursor; pass next_cursor back unchanged as cursor for the next page. next_cursor is null on the last page. limit sets the page size.
Every error has the same shape: {"error": {"code": "not_found", "message": "…", "status": 404}}, with an optional details object. message is written for people; code is stable and meant for programs.
Dates are ISO 8601 in UTC. Phone numbers are E.164 (+16175550100). Call ids are opaque strings; use them exactly as returned.
For safety these stay in the Foyer app only: deleting the account or removing Foyer, purchases and billing (Apple in-app purchase), phone-number verification codes (adding a screened number, confirming an email), exporting data by email, admin tools, answering or controlling a call while it is live, sharing public links, and the phone interview with the host. Reading a call through the API never marks it read; use POST /v1/calls/{id}/mark-read for that.
The key in use, its permissions and this specification.
/v1/openapi.jsonThis specification as JSON
The OpenAPI 3.1 document for this API. No authentication.
200 The OpenAPI document.
curl "https://api.hifoyer.com/v1/openapi.json"/v1/meWhat this key can do
The key in use, which scopes it has, whether the member's plan allows changes, and the rate limits. Call this first to learn what you may do. Needs no particular scope.
200 The key and its permissions.
| Field | Type | Description |
|---|---|---|
data | Me |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/me" \
-H "Authorization: Bearer $FOYER_API_KEY"Screened calls, voicemails, the review queue, search and statistics.
/v1/callsList screened calls
The member's calls, newest first, with who called, what Foyer did with the call (outcome) and a one-line summary. Filter by outcome, date, read state, voicemail or caller. Archived calls are left out unless archived says otherwise. Listing never marks calls read.
| Name | In | Type | Description |
|---|---|---|---|
outcome | query | string | Only calls with these outcomes (comma-separated). See the Outcome schema for what each means. Example: review,voicemail. |
since | query | string (date-time) | Only calls that started at or after this time (ISO 8601). |
until | query | string (date-time) | Only calls that started at or before this time (ISO 8601). |
unread | query | boolean | true for calls the member has not opened yet, false for ones they have. |
has_voicemail | query | boolean | true for calls that left a voicemail. |
archived | query | string | exclude (default) leaves archived calls out, only lists just them, include lists both. One of: exclude, only, include. Default exclude. |
caller | query | string | Only calls from this full phone number. |
limit | query | integer | Items per page, 1-100 (default 25). 1-100. Default 25. |
cursor | query | string | The next_cursor from the previous page, unchanged. Omit for the first page. |
200 One page of calls, newest first.
| Field | Type | Description |
|---|---|---|
data required | array of Call | |
next_cursor required | string, nullable | Pass back as cursor for the next page; null on the last page. |
invalid_request).unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/calls" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/voicemailsList calls that left a voicemail
Calls where the caller left a voicemail, newest first. Same shape and filters as GET /v1/calls. Use voicemail.reviewed to find new ones; read the message text with GET /v1/calls/{id} (needs transcripts:read) or listen with GET /v1/calls/{id}/audio?part=voicemail.
| Name | In | Type | Description |
|---|---|---|---|
since | query | string (date-time) | Only calls that started at or after this time (ISO 8601). |
until | query | string (date-time) | Only calls that started at or before this time (ISO 8601). |
unread | query | boolean | true for calls the member has not opened yet, false for ones they have. |
archived | query | string | exclude (default) leaves archived calls out, only lists just them, include lists both. One of: exclude, only, include. Default exclude. |
caller | query | string | Only calls from this full phone number. |
limit | query | integer | Items per page, 1-100 (default 25). 1-100. Default 25. |
cursor | query | string | The next_cursor from the previous page, unchanged. Omit for the first page. |
200 One page of calls, newest first.
| Field | Type | Description |
|---|---|---|
data required | array of Call | |
next_cursor required | string, nullable | Pass back as cursor for the next page; null on the last page. |
invalid_request).unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/voicemails" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/reviewCalls waiting for the member's review
Calls Foyer was unsure about and held for the member (outcome = review) that the member has not looked at yet, newest first. To clear one, mark it read; to decide what happens next time this caller rings, refile it (POST /v1/calls/{id}/refile).
| Name | In | Type | Description |
|---|---|---|---|
since | query | string (date-time) | Only calls that started at or after this time (ISO 8601). |
until | query | string (date-time) | Only calls that started at or before this time (ISO 8601). |
limit | query | integer | Items per page, 1-100 (default 25). 1-100. Default 25. |
cursor | query | string | The next_cursor from the previous page, unchanged. Omit for the first page. |
200 One page of calls, newest first.
| Field | Type | Description |
|---|---|---|
data required | array of Call | |
next_cursor required | string, nullable | Pass back as cursor for the next page; null on the last page. |
invalid_request).unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/review" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/calls/{id}One call in full
Everything about one call: the caller, the outcome and summary, which forwarding line it came in on, what went wrong if anything (incidents), the voicemail, recordings and which audio parts exist. With transcripts:read it also has the words: the screening conversation between Foyer and the caller, the voicemail text and the transcript of the recorded connected call. Without that scope those fields are left out and transcripts_omitted is true. Reading a call here does NOT mark it read.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The call's id, exactly as returned in a call's id. |
200 The call.
| Field | Type | Description |
|---|---|---|
data | CallDetail |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/calls/CALL_ID" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/searchSearch calls by words
Finds calls whose caller name, caller ID, company, number or summary contain q (case-insensitive, the whole phrase). With transcripts:read it also looks inside voicemail text, the screening conversation and the recorded call's transcript, and each hit carries a short excerpt around the match.
Calls are stored encrypted, so search examines up to 400 calls per request, newest first. If next_cursor is not null, more history remains: pass it back as cursor to keep searching further back. search.reached_oldest_call says when there is nothing older. Combine with since/until or outcome to narrow the scan. Counts against the search limit of 20 a minute.
| Name | In | Type | Description |
|---|---|---|---|
q required | query | string | The words to find, 2-200 characters. Matched as one phrase, ignoring case. Digits also match part of a caller's number (3 or more digits). 2-200 characters. Example: plumber. |
outcome | query | string | Only calls with these outcomes (comma-separated). See the Outcome schema for what each means. Example: review,voicemail. |
since | query | string (date-time) | Only calls that started at or after this time (ISO 8601). |
until | query | string (date-time) | Only calls that started at or before this time (ISO 8601). |
archived | query | string | exclude (default) leaves archived calls out, only lists just them, include lists both. One of: exclude, only, include. Default exclude. |
limit | query | integer | Most matches to return, 1-50 (default 25). 1-50. Default 25. |
cursor | query | string | The next_cursor from the previous page, unchanged. Omit for the first page. |
200 Matching calls, newest first.
| Field | Type | Description |
|---|---|---|
data required | array of SearchHit | |
next_cursor required | string, nullable | Pass back as cursor to keep searching older calls; null when the oldest call was reached. |
search required | object | |
search.searched_fields | array of SearchField | Which parts of each call were searched (transcript fields need transcripts:read). |
search.calls_examined | integer | How many calls this request looked at. |
search.reached_oldest_call | boolean | True when there is no older call left to search. |
search.method | string | How the search ran. One of: scan, index. |
invalid_request).unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/search?q=plumber" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/statsCall counts today and overall
How many calls Foyer handled today (put through, held for review, sent to voicemail, blocked) and since the member joined.
| Name | In | Type | Description |
|---|---|---|---|
exclude_self_tests | query | boolean | true leaves out test calls the member made to their own line. |
200 The counts.
| Field | Type | Description |
|---|---|---|
data | object | |
data.today | object | |
data.today.through | integer | Calls put through to the member. |
data.today.review | integer | Calls held for review. |
data.today.voicemail | integer | Calls sent to voicemail. |
data.today.blocked | integer | Spam or blocked callers turned away. |
data.today.total | integer | |
data.lifetime | object | |
data.lifetime.screened | integer | |
data.lifetime.blocked | integer | |
data.lifetime.since | string (date-time), nullable |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/stats" \
-H "Authorization: Bearer $FOYER_API_KEY"Mark, archive, refile and report calls (calls:write).
/v1/calls/{id}/mark-readMark a call read
Clears the unread dot on every device, exactly like opening the call in the app. Idempotent; returns when it was first read.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The call's id, exactly as returned in a call's id. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/calls/CALL_ID/mark-read" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/calls/{id}/mark-unreadMark a call unread
Shows the call as unread again on every device (and its voicemail as new).
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The call's id, exactly as returned in a call's id. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/calls/CALL_ID/mark-unread" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/calls/{id}/archiveArchive a call
Moves the call out of the member's lists into Archived on every device. Nothing is deleted; unarchive brings it back.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The call's id, exactly as returned in a call's id. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/calls/CALL_ID/archive" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/calls/{id}/unarchiveUnarchive a call
Returns an archived call to the member's lists.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The call's id, exactly as returned in a call's id. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/calls/CALL_ID/unarchive" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/calls/{id}/voicemail/mark-reviewedMark a voicemail heard
Marks this call's voicemail as reviewed, so it no longer counts as a new message.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The call's id, exactly as returned in a call's id. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/calls/CALL_ID/voicemail/mark-reviewed" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/calls/{id}/refileTell Foyer how this caller should be handled
Re-files the call and sets the caller's treatment for next time, the same as the choices on a call in the app: through approves the caller (rings straight through from now on); screened treats them as a stranger (screened every time); review holds their calls for the member's review; voicemail files the call as voicemail; block blocks the caller (turned away from now on). Foyer learns from these choices.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The call's id, exactly as returned in a call's id. |
| Field | Type | Description |
|---|---|---|
decision required | string | One of: through, screened, review, voicemail, block. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/calls/CALL_ID/refile" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"decision":"through"}'/v1/calls/{id}/report-spamReport the caller as spam
Files a spam report for this call's caller, which helps protect every Foyer member. Returns how many households have reported the number. Idempotent.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The call's id, exactly as returned in a call's id. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/calls/CALL_ID/report-spam" \
-H "Authorization: Bearer $FOYER_API_KEY"Short-lived links to recordings (audio:read).
/v1/calls/{id}/audioA five-minute link to a recording
Returns a URL that plays one recording of this call for the next five minutes, without further authentication. Fetch it promptly; it answers with a redirect to the audio file (or the audio itself). Ask again for a new link when it expires. Needs audio:read, which members grant separately. GET /v1/calls/{id} lists the parts that exist in audio_parts.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string | The call's id, exactly as returned in a call's id. |
part required | query | AudioPart | Which recording. voicemail: the message the caller left. screening: Foyer's conversation with the caller. call: the whole call (screening plus the connected conversation when it was recorded). caller / owner: one side of the connected conversation. One of: voicemail, screening, call, caller, owner. |
200 The link.
| Field | Type | Description |
|---|---|---|
data | object | |
data.part required | AudioPart | One of: voicemail, screening, call, caller, owner. |
data.url required | string | Plays the audio until expires_at. Treat it as a secret. |
data.expires_at required | string (date-time) |
invalid_request).unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/calls/CALL_ID/audio?part=voicemail" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/audioPlay a recording from a link
The URL returned by GET /v1/calls/{id}/audio, used as given. The link is its own credential for five minutes; no Authorization header is needed. It stops working when it expires, when the key that made it is revoked or loses audio:read, or when the account closes.
| Name | In | Type | Description |
|---|---|---|---|
link required | query | string | The opaque signed token from the link. |
200 The audio (supports HTTP Range).
not_found, media_not_found).link_expired).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/audio?link=example"Contacts, approved and blocked callers, remembered callers and personal greetings.
/v1/contactsContacts and how Foyer treats them
Everyone who rings straight through (saved contacts and approved callers), sorted by name, with their treatment: through, or appear_unknown while the member has asked Foyer to screen them like a stranger for a while. personalized means they have a personal greeting or answering setup. The first page also lists blocked numbers. Every other caller is a stranger and is screened.
| Name | In | Type | Description |
|---|---|---|---|
limit | query | integer | Contacts per page, 1-500 (default 100). 1-500. Default 100. |
cursor | query | string | The next_cursor from the previous page, unchanged. Omit for the first page. |
200 One page of contacts.
| Field | Type | Description |
|---|---|---|
data required | array of Contact | |
blocked | array of object | Blocked numbers (first page only). |
blocked[].number | string | |
blocked[].treatment | "block" | |
next_cursor required | string, nullable | |
total required | integer | Contacts across all pages. |
invalid_request).unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/contacts" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/contactsAdd a contact
Saves a contact; they ring straight through from now on. It is also added to the member's iPhone contacts at the next sync.
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
name | string | Full name as it should appear. |
first_name | string | 0-200 characters. |
last_name | string | 0-200 characters. |
business_name | string | 0-200 characters. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/contacts" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100"}'/v1/contacts/archiveRemove a contact (archive)
Takes the number out of contacts and clears any approval or block, so the caller is screened like a stranger. Reversible with restore.
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/contacts/archive" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100"}'/v1/contacts/restoreRestore an archived contact
Brings an archived contact back under its saved name; they ring straight through again.
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/contacts/restore" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100"}'/v1/contacts/approveLet a caller ring straight through
Approves a number (the "Through" choice on a contact). Clears a block on it.
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
name | string | Optional name to save with the approval. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/contacts/approve" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100"}'/v1/contacts/unapproveScreen a caller again
Removes the approval, so Foyer screens this number like any stranger. To hold their calls for review or block them, refile one of their calls instead.
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/contacts/unapprove" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100"}'/v1/contacts/appear-unknownScreen a contact like a stranger for a while
For the next hours, this contact is screened like an unknown caller instead of ringing straight through. hours 0 ends it now.
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
hours required | number | 0-720. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/contacts/appear-unknown" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100","hours":0}'/v1/contacts/preferencesChange one contact's preferences
Per-contact options for a saved contact. record_always records every call with this contact (a recording notice plays first; available on accounts in the preview). answer_mode personal_ai lets Foyer answer this contact with a personal greeting and script instead of ringing straight through (direct). Send only the fields to change. Some of these depend on the member's plan.
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
record_always | boolean | |
answer_mode | string | One of: direct, personal_ai. |
voice_id | string | Voice for the personal greeting (from GET /v1/voices); empty clears. |
greeting | string | Custom greeting for this contact; empty clears. 0-1000 characters. |
script | string | What Foyer should say or ask; empty clears. 0-8000 characters. |
first_name | string | 0-200 characters. |
last_name | string | 0-200 characters. |
business_name | string | 0-200 characters. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PATCH "https://api.hifoyer.com/v1/contacts/preferences" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100"}'/v1/remembered-callersCallers Foyer remembers
People who are not contacts but have called before and gave a name; Foyer greets them by it next time. Up to 200, most recent first.
200 The remembered callers.
| Field | Type | Description |
|---|---|---|
data | object | |
data.enabled | boolean | Whether returning-caller recognition is on. |
data.callers | array of object | |
data.callers[].id | string (uuid) | |
data.callers[].name | string, nullable | |
data.callers[].name_source | string, nullable | owner when the member set the name |
data.callers[].number | string, nullable | |
data.callers[].last_contact | object, nullable | When they last called and why. |
data.callers[].updated_at | string (date-time), nullable |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/remembered-callers" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/remembered-callers/{id}Correct a remembered caller's name
The name Foyer greets this caller by. 1-3 words of letters, up to 40 characters.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string (uuid) |
| Field | Type | Description |
|---|---|---|
name required | string | 0-40 characters. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PATCH "https://api.hifoyer.com/v1/remembered-callers/ID" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Dave"}'/v1/remembered-callers/{id}Forget a remembered caller
forget=name forgets only the name; forget=all (default) forgets the caller entirely.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string (uuid) | |
forget | query | string | name forgets only the name (the last call stays on record); all forgets the caller entirely. One of: name, all. Default all. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X DELETE "https://api.hifoyer.com/v1/remembered-callers/ID" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/answering-profilesSaved personal greetings
Reusable greetings and scripts the member saved for answering particular contacts.
200 The saved greetings.
| Field | Type | Description |
|---|---|---|
data | array of object | |
data[].id | string (uuid) | |
data[].name | string | |
data[].voice_id | string, nullable | |
data[].greeting | string | |
data[].script | string | |
data[].updated_at | string (date-time), nullable |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/answering-profiles" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/answering-profilesSave a personal greeting
Saves a reusable greeting and/or script. At least one of greeting, script or voice_id is required.
| Field | Type | Description |
|---|---|---|
name | string | 0-60 characters. |
greeting | string | 0-1000 characters. |
script | string | 0-8000 characters. |
voice_id | string |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/answering-profiles" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'/v1/answering-profiles/{id}Remove a saved personal greeting
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string (uuid) |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X DELETE "https://api.hifoyer.com/v1/answering-profiles/ID" \
-H "Authorization: Bearer $FOYER_API_KEY"The member's name, plan, numbers and every setting the app can change.
/v1/accountThe member's name, plan and Foyer number
Names (and the name callers hear), pronouns, the Foyer number calls are forwarded to, and the plan with its API access.
200 The account.
| Field | Type | Description |
|---|---|---|
data | Account |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/account" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/account/profileChange the member's name
First, last and preferred name. The preferred name (or first and last) is what Foyer tells callers it answers for. At least one name is required; pronouns is optional.
| Field | Type | Description |
|---|---|---|
first_name | string | 0-100 characters. |
last_name | string | 0-100 characters. |
preferred_name | string | 0-120 characters. |
pronouns | Pronouns | How Foyer refers to the member; null means it just uses their name. One of: he, she, they, private. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PATCH "https://api.hifoyer.com/v1/account/profile" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'/v1/account/pronounsHow Foyer refers to the member
| Field | Type | Description |
|---|---|---|
pronouns required | Pronouns | How Foyer refers to the member; null means it just uses their name. One of: he, she, they, private. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/account/pronouns" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"pronouns":"he"}'/v1/settingsAll settings
Screening level and host, the greeting callers hear, recording, ringing, spam handling and notifications, grouped as in the app.
200 The settings.
| Field | Type | Description |
|---|---|---|
data | Settings |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/settings" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/settings/openingsGreeting choices
The preset opening lines callers can hear (with how each sounds for this member) and the current choice. Use a preset key with PUT /v1/settings/opening.
200 The information as the Foyer app shows it.
| Field | Type | Description |
|---|---|---|
data | object |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/settings/openings" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/settings/screening-hostsAI models that can screen calls
The screening hosts (AI models) available on this account, which one is in use, and whether an administrator locked the choice.
200 The information as the Foyer app shows it.
| Field | Type | Description |
|---|---|---|
data | object |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/settings/screening-hosts" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/voicesVoices Foyer can speak in
The voices available for answering calls and the current one. Use an id with PUT /v1/settings/voice.
200 The voices.
| Field | Type | Description |
|---|---|---|
data | object | |
data.current | object, nullable | |
data.voices | array of object | |
data.voices[].id | string | |
data.voices[].name | string | |
data.voices[].description | string | |
data.voices[].gender | string | |
data.voices[].accent | string |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/voices" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/settings/screening-levelHow strict screening is
Send level on the 1-5 scale (1-2 lenient, 3 balanced, 4-5 strict) or level_3 on the three-level scale (1 Open, 2 Balanced, 3 Strict; stored as 1, 3 and 5). GET /v1/settings says which scale the member sees (screening.level_scale).
| Field | Type | Description |
|---|---|---|
level | integer | 1-5. |
level_3 | integer | 1-3. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/screening-level" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'/v1/settings/screening-hostWhich AI model screens calls
| Field | Type | Description |
|---|---|---|
host required | string | A key from GET /v1/settings/screening-hosts. One of: qwen, gemini, sonnet. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/screening-host" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"host":"qwen"}'/v1/settings/openingThe first thing callers hear
A preset (classic, warm, concise, butler) or custom with your own text (1-8000 characters). Foyer adds the recording notice when one is required.
| Field | Type | Description |
|---|---|---|
key required | string | One of: classic, warm, concise, butler, custom. |
text | string | 0-8000 characters. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/opening" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"key":"classic"}'/v1/settings/identity-styleHow much Foyer says about who it answers for
friendly uses the member's name, balanced is more reserved, anonymous never says who it answers for.
| Field | Type | Description |
|---|---|---|
style required | string | One of: friendly, balanced, anonymous. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/identity-style" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"style":"friendly"}'/v1/settings/recordingWhich calls are recorded
The call outcomes whose calls are recorded. Send the full list; an empty list records none. known (saved contacts) is only available on lines that support it.
| Field | Type | Description |
|---|---|---|
record_outcomes required | array of string |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/recording" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"record_outcomes":["known"]}'/v1/settings/known-callersOptions for calls from saved contacts
transcription_enabled records and transcribes calls with saved contacts (where available). greeting_enabled plays a short greeting before putting a contact through. Send one or both.
| Field | Type | Description |
|---|---|---|
transcription_enabled | boolean | |
greeting_enabled | boolean |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PATCH "https://api.hifoyer.com/v1/settings/known-callers" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'/v1/settings/notificationsEmail and push notifications
A partial update merged into the member's notification choices, for example {"categories": {"voicemail": {"push": false}}}. Categories: voicemail, call.known, call.connect, call.review, call.voicemail_tier, call.spam, call.live, recordings, daily_digest, product_updates; each has email and/or push (see notifications.channels_available in GET /v1/settings). channels are master switches; options include voicemail_include_transcript, voicemail_include_audio_link and live_note_include_words. Unknown keys are refused.
| Field | Type | Description |
|---|---|---|
channels | object | |
channels.email | boolean | |
channels.push | boolean | |
categories | object | |
options | object |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PATCH "https://api.hifoyer.com/v1/settings/notifications" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'/v1/settings/live-call-alertsAlerts while a call is being screened
| Field | Type | Description |
|---|---|---|
mode required | string | One of: off, live_activity, notification. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/live-call-alerts" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode":"off"}'/v1/settings/app-badgeWhat the app icon badge counts
| Field | Type | Description |
|---|---|---|
mode required | string | One of: voicemail, missed, all, off. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/app-badge" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode":"voicemail"}'/v1/settings/announcementsPush notifications for Foyer announcements
| Field | Type | Description |
|---|---|---|
push_enabled required | boolean |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/announcements" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"push_enabled":true}'/v1/settings/pausePause screening
While paused, every call rings straight through without screening (for example while expecting a verification call). indefinite stays paused until resumed; Foyer reminds the member daily.
| Field | Type | Description |
|---|---|---|
preset required | string | One of: 10m, 1h, 8h, 24h, indefinite. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/pause" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"preset":"10m"}'/v1/settings/pauseResume screening now
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X DELETE "https://api.hifoyer.com/v1/settings/pause" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/settings/safe-wordThe safe word
A word or short phrase (up to 60 characters) that lets any caller who says it straight through without screening. Pick something only real contacts would know. An empty string clears it.
| Field | Type | Description |
|---|---|---|
word required | string | 0-60 characters. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/safe-word" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"word":"example"}'/v1/settings/voiceThe voice Foyer answers in
| Field | Type | Description |
|---|---|---|
voice_id required | string | An id from GET /v1/voices. |
speed | number | Optional speaking speed (1 is normal). 0.6-1.4. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/voice" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"voice_id":"example"}'/v1/settings/voice-speedHow fast Foyer speaks
| Field | Type | Description |
|---|---|---|
speed required | number | 0.6-1.4. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/voice-speed" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"speed":0.6}'/v1/settings/ringingWhere approved calls ring
both (the Foyer app and a SIP phone), app, groundwire (SIP phone only) or none.
| Field | Type | Description |
|---|---|---|
mode required | string | One of: both, app, groundwire, none. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/ringing" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode":"both"}'/v1/settings/browser-ringingRing the web dashboard too
| Field | Type | Description |
|---|---|---|
enabled required | boolean |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/browser-ringing" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":true}'/v1/settings/caller-name-lookupLook up caller ID names
| Field | Type | Description |
|---|---|---|
enabled required | boolean |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/caller-name-lookup" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":true}'/v1/settings/spam-auto-reportReport spam calls automatically
On some accounts this is always on and the stored choice is ignored; the response says the value in effect.
| Field | Type | Description |
|---|---|---|
enabled required | boolean |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/spam-auto-report" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":true}'/v1/settings/do-not-callRecord that the number is on the Do Not Call registry
The member's own statement that their number is registered at donotcall.gov. Foyer cannot check the registry.
| Field | Type | Description |
|---|---|---|
registered required | boolean |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/settings/do-not-call" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"registered":true}'/v1/numbersFoyer number and forwarding lines
The Foyer number calls are forwarded to; the member's phone lines that forward to it, each with whether forwarding is working (forwarding_status: verified, not_forwarding or unchecked) and how to turn it on or off with that carrier; and any extra numbers that ring when a call is put through.
200 The numbers.
| Field | Type | Description |
|---|---|---|
data | Numbers |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/numbers" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/numbers/forwarding-linesAdd a phone line that forwards to Foyer
Records a line the member forwards to their Foyer number. Turning forwarding on happens with the carrier; GET /v1/numbers shows the code to dial.
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
label | string | For example "Work cell". 0-60 characters. |
type | string | One of: cell, googlevoice, landline, other. |
carrier | string | |
forward_code | string | 0-40 characters. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/numbers/forwarding-lines" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100"}'/v1/numbers/forwarding-lines/labelRename a forwarding line
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
label required | string | 1-60 characters. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PATCH "https://api.hifoyer.com/v1/numbers/forwarding-lines/label" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100","label":"example"}'/v1/numbers/forwarding-lines/removeRemove a forwarding line from Foyer
Removes the line from the member's account. It does not turn off forwarding at the carrier; calls already forwarded still reach Foyer.
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/numbers/forwarding-lines/remove" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100"}'/v1/numbers/carrierSet a line's carrier
The carrier of the member's primary line (or number when given), so Foyer can show the right forwarding codes.
| Field | Type | Description |
|---|---|---|
carrier required | string | 0-40 characters. |
forward_code | string | 0-40 characters. |
number | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/numbers/carrier" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"carrier":"example"}'/v1/numbers/screened-carrierSet the carrier of an extra screened number
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
carrier required | string | One of: verizon, att, tmobile, uscellular, googlefi, googlevoice, visible, mint, cricket, boost, metro, xfinity, spectrum, other. |
forward_code | string | 0-40 characters. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/numbers/screened-carrier" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100","carrier":"verizon"}'/v1/numbers/ring-destinationRing an extra verified number
Turns ringing on or off for a separately verified number that is not a forwarding line.
| Field | Type | Description |
|---|---|---|
number required | string | A phone number, ideally E.164 (+16175550100). US numbers may omit +1. |
enabled required | boolean |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/numbers/ring-destination" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+16175550100","enabled":true}'/v1/numbers/ring-orderThe order extra numbers ring in
Numbers first to last. Only verified, enabled ring destinations are ordered; others are skipped. Depends on the plan.
| Field | Type | Description |
|---|---|---|
numbers required | array of string |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/numbers/ring-order" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"numbers":["+16175550100"]}'Notes for the host (the AI screener) and the facts it knows about the member.
/v1/notesNotes for the host and what it understands
The member's notes to their host (the AI screener), for example "My plumber Dave will call this week, put him through", and directives, the host's working summary of all of them.
200 The notes.
| Field | Type | Description |
|---|---|---|
data | object | |
data.notes | array of object | |
data.notes[].id | string | |
data.notes[].text | string | |
data.notes[].summary | string | The host's one-line reading of the note. |
data.notes[].created_at | string (date-time), nullable | |
data.notes[].kind | string | |
data.notes[].source | string | |
data.directives | string | What the host currently follows |
data.directives_updated_at | string (date-time), nullable |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/notes" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/notesTell the host something
Adds a note (3-2000 characters) the host takes into account on future calls. The response includes the host's reply and its updated directives.
| Field | Type | Description |
|---|---|---|
text required | string | 3-2000 characters. |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/notes" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"My plumber Dave will call this week. Put him through."}'/v1/notes/{id}Remove a note
Archives the note and recompiles the host's directives without it.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X DELETE "https://api.hifoyer.com/v1/notes/ID" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/notes/directivesEdit the host's directives directly
Replaces the compiled directives with this text as written. The next added or removed note recompiles over it.
| Field | Type | Description |
|---|---|---|
directives required | string |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/notes/directives" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"directives":"example"}'/v1/profileFacts the host knows about the member
The facts the host uses when screening (family, pets, providers, vendors the member expects, where they live and work), including suggestions it learned from calls that wait for the member's decision, and whether call learning is on.
200 The information as the Foyer app shows it.
| Field | Type | Description |
|---|---|---|
data | object |
unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).rate_limited). Wait the number of seconds in Retry-After.curl "https://api.hifoyer.com/v1/profile" \
-H "Authorization: Bearer $FOYER_API_KEY"/v1/profile/short-formEdit the member's key facts
Keys present are replaced; missing keys are left alone; empty values clear. List keys (arrays of short strings): spouse, wife, mother, father, children, pets, expectedVendors, activities. Text keys: location, industry, work.
| Field | Type | Description |
|---|---|---|
facts required | object |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PATCH "https://api.hifoyer.com/v1/profile/short-form" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"facts":{}}'/v1/profile/facts/{id}/decisionAccept, edit or drop a fact
accept a suggested fact, edit it (send value), archive it, reject a suggestion, or delete a fact.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string (uuid) |
| Field | Type | Description |
|---|---|---|
decision required | string | One of: accept, edit, archive, reject, delete. |
value | string | |
target_key | string |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X POST "https://api.hifoyer.com/v1/profile/facts/ID/decision" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"decision":"accept"}'/v1/profile/learningLet the host learn from calls
When on (and available on the member's line), the host suggests facts from calls. Turning it off stops using learned facts right away.
| Field | Type | Description |
|---|---|---|
enabled required | boolean |
200 Done. data is what Foyer saved, in the same shape the Foyer app receives (field names may be camelCase). Re-read the resource for the API's own shape.
| Field | Type | Description |
|---|---|---|
data required | object |
invalid_request).unauthorized).plan_required).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).not_found, media_not_found).conflict, key_limit).rate_limited). Wait the number of seconds in Retry-After.server_error, unavailable). Safe to retry later.curl -X PUT "https://api.hifoyer.com/v1/profile/learning" \
-H "Authorization: Bearer $FOYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":true}'Key management for the member's own signed-in Foyer app or dashboard (not usable with an API key).
/v1/keysThe member's API keys
Every key the member made, newest first, with its status. Never shows the secret. Needs the member's signed-in app session.
200 The keys.
| Field | Type | Description |
|---|---|---|
data | array of ApiKey | |
available | boolean | Whether this account may make keys now. |
writes_allowed | boolean | Whether the plan includes changes now, so a new key may have write scopes (Concierge, Chief of Staff, the free period). |
plan | string | The plan's name |
unauthorized).curl "https://api.hifoyer.com/v1/keys" \
-H "Authorization: Bearer $FOYER_APP_SESSION"/v1/keysMake an API key
Makes a key with the chosen scopes. The secret is in this response only; Foyer stores just a fingerprint of it. Write scopes need a plan that includes changes (403 plan_does_not_include_writes otherwise). At most 10 active keys.
| Field | Type | Description |
|---|---|---|
name required | string | What the key is for, e.g. "Claude". 1-60 characters. |
scopes required | array of Scope | |
expires_in_days | integer | 1-90. Default 90. |
201 The new key, with its secret (shown once).
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.kind | string | key: a personal API key. app: an app connected by signing in (MCP). One of: key, app. |
data.name | string | The key's name |
data.key_hint | string, nullable | The first characters of the key |
data.app | object | Connected apps only (kind app). |
data.app.name | string | The name the app registered with. |
data.app.host | string, nullable | Where the app sends the member back after sign-in |
data.scopes | array of Scope | |
data.status | string | One of: active, revoked, expired. |
data.created_at | string (date-time) | |
data.last_used_at | string (date-time), nullable | |
data.expires_at | string (date-time) | |
data.revoked_at | string (date-time), nullable | |
data.secret | string | The key (fyr_…). Shown once. |
invalid_request).unauthorized).insufficient_scope: the key lacks the scope named in details.required_scope. plan_does_not_include_writes: the member's plan reads only, nothing was changed. api_not_enabled: the API is not open on this account yet. forbidden: the app itself refuses this change (for example a feature not available on this line).conflict, key_limit).curl -X POST "https://api.hifoyer.com/v1/keys" \
-H "Authorization: Bearer $FOYER_APP_SESSION" \
-H "Content-Type: application/json" \
-d '{"name":"Dave","scopes":["calls:read"]}'/v1/keys/{id}/revokeRevoke an API key
The key stops working immediately, including audio links it made. Idempotent.
| Name | In | Type | Description |
|---|---|---|---|
id required | path | string (uuid) |
200 The revoked key.
| Field | Type | Description |
|---|---|---|
data | ApiKey | A personal API key (kind = key) or an app the member connected by signing in, such as Claude or ChatGPT through the Foyer MCP connector (kind = app). Both are listed, logged and revoked the same way. |
unauthorized).not_found, media_not_found).curl -X POST "https://api.hifoyer.com/v1/keys/ID/revoke" \
-H "Authorization: Bearer $FOYER_APP_SESSION"/v1/keys/activityWhat each key did
For each key, requests in the last days and what they read or changed in plain words ("read 14 calls, 3 voicemails, searched twice"), plus the 50 most recent requests. The log records endpoints, counts and status only, never content.
| Name | In | Type | Description |
|---|---|---|---|
days | query | integer | How many days back to summarize, 1-90 (default 30). 1-90. Default 30. |
200 The activity.
| Field | Type | Description |
|---|---|---|
data | object | |
data.days | integer | |
data.keys | array of object | |
data.keys[].id | string (uuid) | |
data.keys[].kind | string | key: a personal API key. app: an app connected by signing in (MCP). One of: key, app. |
data.keys[].name | string | The key's name |
data.keys[].key_hint | string, nullable | The first characters of the key |
data.keys[].app | object | Connected apps only (kind app). |
data.keys[].app.name | string | The name the app registered with. |
data.keys[].app.host | string, nullable | Where the app sends the member back after sign-in |
data.keys[].scopes | array of Scope | |
data.keys[].status | string | One of: active, revoked, expired. |
data.keys[].created_at | string (date-time) | |
data.keys[].last_used_at | string (date-time), nullable | |
data.keys[].expires_at | string (date-time) | |
data.keys[].revoked_at | string (date-time), nullable | |
data.keys[].requests | integer | |
data.keys[].errors | integer | |
data.keys[].last_request_at | string (date-time), nullable | |
data.keys[].records_read | object | |
data.keys[].searches | integer | |
data.keys[].changes | integer | |
data.keys[].summary | string | |
data.recent | array of object | |
data.recent[].key_id | string | |
data.recent[].at | string (date-time) | |
data.recent[].method | string | |
data.recent[].endpoint | string | |
data.recent[].status | integer | |
data.recent[].resource | string, nullable | |
data.recent[].records | integer | |
data.recent[].error_code | string, nullable |
invalid_request).unauthorized).curl "https://api.hifoyer.com/v1/keys/activity" \
-H "Authorization: Bearer $FOYER_APP_SESSION"| Field | Type | Description |
|---|---|---|
error required | object | |
error.code required | string | Stable code: unauthorized, api_not_enabled, insufficient_scope, plan_does_not_include_writes, plan_required, forbidden, invalid_request, not_found, media_not_found, conflict, key_limit, link_expired, rate_limited, unavailable, server_error. |
error.message required | string | A sentence for people. |
error.status required | integer | The HTTP status. |
error.details | object |
string. One of: calls:read, transcripts:read, audio:read, contacts:read, settings:read, calls:write, contacts:write, settings:write, notes:write.
A phone number, ideally E.164 (+16175550100). US numbers may omit +1.
string.
How Foyer refers to the member; null means it just uses their name.
string, nullable. One of: he, she, they, private.
What Foyer did with the call. known: a saved contact, put straight through. connect: screened, then put through to the member. review: Foyer was unsure and held the call for the member. voicemail / gate: sent to voicemail (the caller may or may not have left one). honeypot: screened as spam and turned away. fallback: a blocked caller, turned away.
string. One of: known, connect, review, voicemail, gate, honeypot, fallback.
string. One of: voicemail, screening, call, caller, owner.
string. One of: name, caller_id_name, company, number, summary, voicemail, screening_conversation, connected_call.
A screened call as it appears in lists.
| Field | Type | Description |
|---|---|---|
id required | string | The call's id. |
started_at required | string (date-time), nullable | |
ended_at | string (date-time), nullable | |
duration_sec | integer | |
caller required | object | |
caller.number | string, nullable | The caller's number (E.164) |
caller.name | string, nullable | The name Foyer knows them by (contact |
caller.caller_id_name | string, nullable | The carrier's caller ID name. |
caller.company | string, nullable | The business they said they call from. |
caller.identity_source | string | Where name came from (contact |
caller.city | string, nullable | |
caller.state | string, nullable | |
outcome required | Outcome | What Foyer did with the call. known: a saved contact, put straight through. connect: screened, then put through to the member. review: Foyer was unsure and held the call for the member. voicemail / gate: sent to voicemail (the caller may or may not have left one). honeypot: screened as spam and turned away. fallback: a blocked caller, turned away. One of: known, connect, review, voicemail, gate, honeypot, fallback. |
outcome_label | string | The outcome in plain words. |
refiled_as | string, nullable | The member's own decision on this call, if they refiled it. One of: through, screened, review, voicemail, block. |
summary required | string | One or two sentences on who called and why. |
spam_score | number, nullable | 0-1 likelihood the caller is spam |
self_test | boolean | The member called their own line to test it. |
read required | boolean | The member has opened this call. |
archived required | boolean | |
voicemail required | object | |
voicemail.state | string | transcribed message with text; audio_only message without text yet; captured being processed; not_left sent to voicemail but no message; none. One of: transcribed, audio_only, captured, not_left, none. |
voicemail.has_audio | boolean | |
voicemail.reviewed | boolean | The member has heard it. |
has_screening_recording | boolean | |
has_call_recording | boolean | |
has_connected_transcript | boolean | The connected conversation was recorded and transcribed. |
record_always | boolean | Recorded because the caller is a contact set to always record. |
hangup | object | How and when the call ended |
A screened call as it appears in lists.
| Field | Type | Description |
|---|---|---|
id required | string | The call's id. |
started_at required | string (date-time), nullable | |
ended_at | string (date-time), nullable | |
duration_sec | integer | |
caller required | object | |
caller.number | string, nullable | The caller's number (E.164) |
caller.name | string, nullable | The name Foyer knows them by (contact |
caller.caller_id_name | string, nullable | The carrier's caller ID name. |
caller.company | string, nullable | The business they said they call from. |
caller.identity_source | string | Where name came from (contact |
caller.city | string, nullable | |
caller.state | string, nullable | |
outcome required | Outcome | What Foyer did with the call. known: a saved contact, put straight through. connect: screened, then put through to the member. review: Foyer was unsure and held the call for the member. voicemail / gate: sent to voicemail (the caller may or may not have left one). honeypot: screened as spam and turned away. fallback: a blocked caller, turned away. One of: known, connect, review, voicemail, gate, honeypot, fallback. |
outcome_label | string | The outcome in plain words. |
refiled_as | string, nullable | The member's own decision on this call, if they refiled it. One of: through, screened, review, voicemail, block. |
summary required | string | One or two sentences on who called and why. |
spam_score | number, nullable | 0-1 likelihood the caller is spam |
self_test | boolean | The member called their own line to test it. |
read required | boolean | The member has opened this call. |
archived required | boolean | |
voicemail required | object | |
voicemail.duration_sec | number, nullable | |
voicemail.reviewed_at | string (date-time), nullable | |
voicemail.transcript | string | The message text (transcripts:read). |
has_screening_recording | boolean | |
has_call_recording | boolean | |
has_connected_transcript | boolean | The connected conversation was recorded and transcribed. |
record_always | boolean | Recorded because the caller is a contact set to always record. |
hangup | object | How and when the call ended |
read_at | string (date-time), nullable | |
to | string, nullable | The Foyer number the call reached. |
forwarded_from | string, nullable | The member's line that forwarded the call |
line | object, nullable | The member's forwarding line the caller dialled, when identified. |
line.name | string, nullable | |
line.number | string, nullable | |
incidents | array of object | Anything that went wrong or was degraded on this call, in plain words, most serious first. |
recordings | object | |
recordings.screening | object, nullable | |
recordings.call | object, nullable | |
audio_parts | array of AudioPart | The recordings GET /v1/calls/{id}/audio can play for this call. |
connected_call | object, nullable | The conversation after Foyer put the caller through, when it was recorded. |
connected_call.started_at | string (date-time), nullable | |
connected_call.transcript_status | string, nullable | |
connected_call.transcript | array of object | Both sides in order (transcripts:read). |
connected_call.transcript[].speaker | string | One of: caller, you. |
connected_call.transcript[].text | string | |
connected_call.transcript[].start_sec | number, nullable | |
connected_call.transcript[].at | string (date-time), nullable | |
connected_call.sides | array of object | Each side's text on its own, when the two could not be put in order (transcripts:read). |
connected_call.sides[].speaker | string | One of: caller, you. |
connected_call.sides[].text | string | |
screening_conversation | array of object | What Foyer and the caller said while screening, in order (transcripts:read). |
screening_conversation[].speaker | string | One of: caller, foyer. |
screening_conversation[].text | string | |
screening_conversation[].at | string (date-time), nullable | |
transcripts_omitted | boolean | True when the key lacks transcripts:read, so the words were left out. |
| Field | Type | Description |
|---|---|---|
call | Call | A screened call as it appears in lists. |
matched_in | array of SearchField | |
excerpt | string, nullable | A short passage around the first match (not for number matches). |
| Field | Type | Description |
|---|---|---|
number | string | |
name | string, nullable | |
treatment | string | through rings straight through; appear_unknown is screened like a stranger until appear_unknown_until. One of: through, appear_unknown. |
appear_unknown_until | string (date-time), nullable | |
personalized | boolean | Has a personal greeting or answering setup. |
| Field | Type | Description |
|---|---|---|
member_id | string (uuid) | |
first_name | string, nullable | |
last_name | string, nullable | |
preferred_name | string, nullable | |
display_name | string, nullable | |
name_callers_hear | string, nullable | The name Foyer says it answers for. |
pronouns | Pronouns | How Foyer refers to the member; null means it just uses their name. One of: he, she, they, private. |
foyer_number | string, nullable | The Foyer number the member's lines forward to. |
plan | object | |
plan.name | string | Doorman |
plan.key | string | |
plan.api_access | string | One of: read, write. |
plan.basis | string |
| Field | Type | Description |
|---|---|---|
member_id | string (uuid) | |
key | object, nullable | |
key.id | string (uuid) | |
key.name | string | |
key.scopes | array of Scope | |
key.expires_at | string (date-time) | |
access | object | |
access.level | string | What the member's plan allows through the API. One of: read, write. |
access.plan | string | |
access.plan_key | string | |
access.basis | string | Why (plan, free_period, complimentary, grace, trial, no_active_plan, billing_unknown). |
access.writes_allowed | boolean | |
scopes | array of object | |
scopes[].scope | Scope | One of: calls:read, transcripts:read, audio:read, contacts:read, settings:read, calls:write, contacts:write, settings:write, notes:write. |
scopes[].granted | boolean | |
scopes[].description | string | |
rate_limits | object | |
rate_limits.per_minute | integer | |
rate_limits.per_day | integer | |
rate_limits.searches_per_minute | integer |
| Field | Type | Description |
|---|---|---|
screening | object | |
screening.level | integer, nullable | Stored strictness 1-5. |
screening.level_scale | integer | Which scale the member's app shows. One of: 3, 5. |
screening.level_3 | integer, nullable | The same choice on the three-level scale. |
screening.host | string, nullable | The AI model screening calls. |
screening.safe_word | string, nullable | |
screening.pause | object, nullable | Whether screening is paused and until when. |
greeting | object | |
greeting.opening_key | string | |
greeting.opening_text | string | |
greeting.opener_preview | object, nullable | What callers hear |
greeting.identity_style | string, nullable | |
greeting.owner_pronouns | Pronouns | How Foyer refers to the member; null means it just uses their name. One of: he, she, they, private. |
greeting.voice_id | string, nullable | |
greeting.voice_speed | number | |
recording | object | |
recording.record_outcomes | array of string | |
recording.known_caller_transcription | boolean | |
recording.known_caller_greeting | boolean | |
recording.known_caller_transcription_available | boolean | |
ringing | object | |
ringing.mode | string | One of: both, app, groundwire, none. |
ringing.browser | boolean | |
ringing.iphone_ready | boolean | An iPhone can ring right now. |
spam | object | |
spam.auto_report | boolean | |
spam.do_not_call_registered_at | string (date-time), nullable | |
spam.caller_name_lookup | boolean | |
notifications | object | |
notifications.preferences | object | The full notification choices (channels |
notifications.channels_available | object | Which channels each category supports. |
notifications.live_call_alerts | string | One of: off, live_activity, notification. |
notifications.app_badge | string, nullable | One of: voicemail, missed, all, off. |
notifications.announcements_push | boolean, nullable |
| Field | Type | Description |
|---|---|---|
foyer_number | string, nullable | |
forwarding_lines | array of object | |
forwarding_lines[].number | string, nullable | |
forwarding_lines[].label | string | |
forwarding_lines[].type | string | |
forwarding_lines[].carrier | string | |
forwarding_lines[].verified | boolean | |
forwarding_lines[].forwarding_status | string | One of: verified, not_forwarding, unchecked, disconnected. |
forwarding_lines[].forwarding_status_at | string (date-time), nullable | |
forwarding_lines[].last_checked_at | string (date-time), nullable | |
forwarding_lines[].forwarding_verified_at | string (date-time), nullable | |
forwarding_lines[].turn_on | object, nullable | How to switch forwarding on with this carrier. |
forwarding_lines[].turn_off | object, nullable | How to switch it off. |
ring_destinations | array of object | |
ring_destinations[].number | string, nullable | |
ring_destinations[].label | string | |
ring_destinations[].verified | boolean | |
ring_destinations[].ring_enabled | boolean | |
ring_destinations[].priority | integer |
A personal API key (kind = key) or an app the member connected by signing in, such as Claude or ChatGPT through the Foyer MCP connector (kind = app). Both are listed, logged and revoked the same way.
| Field | Type | Description |
|---|---|---|
id | string (uuid) | |
kind | string | key: a personal API key. app: an app connected by signing in (MCP). One of: key, app. |
name | string | The key's name |
key_hint | string, nullable | The first characters of the key |
app | object | Connected apps only (kind app). |
app.name | string | The name the app registered with. |
app.host | string, nullable | Where the app sends the member back after sign-in |
scopes | array of Scope | |
status | string | One of: active, revoked, expired. |
created_at | string (date-time) | |
last_used_at | string (date-time), nullable | |
expires_at | string (date-time) | |
revoked_at | string (date-time), nullable |