Parley Notes API, version 1.Read notes, create notes, and hear when one is ready.
The Parley Notes API lets an app you connect read your meeting notes, create notes from text or audio, and be told the moment a note is ready. It is what the Parley Notes integration for Zapier is built on, and any app registered with Parley Notes can use it the same way.
- Base URL:
https://app.parleynotes.com/api/v1 - Format: JSON in and out, UTF-8. Send
Content-Type: application/jsonwith a body. - Authentication: OAuth 2.0, authorization code grant. Every call carries
Authorization: Bearer <access token>. - Health notes are never returned. A note written as a health conversation (a doctor visit, a therapy session, a note in a folder analysed as medical) is not listed, found, read or sent to a hook, by any endpoint.
1. Authentication
Apps are registered by hand: Parley Notes gives each app a client id and a client secret (write to the Parley Notes team to register one). There is no self-service registration for this API.
Scopes
| Scope | Allows |
|---|---|
notes:read | GET /me, GET /notes, GET /notes/find, GET /notes/{id}, GET /action-items, GET /hooks/sample |
notes:write | POST /notes |
hooks | POST /hooks, DELETE /hooks/{id} |
Ask for several scopes separated by spaces (commas work too). Asking for none grants all three.
Step 1: send the person to authorize
GET https://app.parleynotes.com/api/v1/oauth/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fyour.app%2Foauth%2Fcallback
&response_type=code
&scope=notes%3Aread%20notes%3Awrite%20hooks
&state=YOUR_STATE
The person signs in to Parley Notes (with their usual sign-in) and sees what your app asks for. If they allow it, Parley Notes redirects to your redirect_uri with a code and your state exactly as you sent it:
https://your.app/oauth/callback?code=pn_code_...&state=YOUR_STATE
If they cancel: ?error=access_denied&error_description=...&state=YOUR_STATE. The redirect_uri must be one registered for your app. PKCE (code_challenge with code_challenge_method=S256) is supported and checked when you send it.
Step 2: exchange the code (within 5 minutes, once)
curl -X POST https://app.parleynotes.com/api/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=authorization_code \
-d code=pn_code_... \
-d redirect_uri=https://your.app/oauth/callback \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET
The client may authenticate with HTTP Basic instead of the two body fields. JSON bodies are accepted too.
{
"access_token": "pn_at_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "pn_rt_...",
"scope": "notes:read notes:write hooks"
}
Step 3: call the API
curl https://app.parleynotes.com/api/v1/me -H "Authorization: Bearer pn_at_..."
Refreshing
Access tokens last one hour. When a call answers 401, refresh and try again:
curl -X POST https://app.parleynotes.com/api/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=refresh_token \
-d refresh_token=pn_rt_... \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET
The answer is a new pair. Refresh tokens rotate: keep the new refresh token and drop the old one. Reusing an old refresh token more than a minute after it was exchanged ends the connection (it is treated as a stolen copy); two refreshes racing each other within that minute both succeed. A refresh token unused for 90 days expires.
| Situation | Answer |
|---|---|
| Access token missing, unknown, expired or revoked | 401 invalid_token, with WWW-Authenticate: Bearer ... (never 403) |
| Refresh token unknown, expired, reused or revoked | 400 {"error": "invalid_grant"} |
Code unknown, used, expired, or another redirect_uri | 400 {"error": "invalid_grant"} |
| Wrong client id or secret at the token endpoint | 401 {"error": "invalid_client"} |
Revoking
curl -X POST https://app.parleynotes.com/api/v1/oauth/revoke -d token=pn_rt_...
Always answers 200; the connection ends. The person can also disconnect your app at any time in Parley Notes, under Settings, Connections, Connected apps; its tokens stop working at once and its hooks stop.
2. The note object
Every note has every key below on every response. A value that is empty is "" (text), [] (list) or 0 (duration), never null, so a field mapped in a workflow never disappears.
| Field | Type | What it is |
|---|---|---|
id | string | The note's id |
title | string | The note's title |
date_iso | string | When the note was recorded or made, ISO 8601 in UTC |
duration_min | integer | Length in minutes (0 for a note that was not spoken) |
occasion | string | What the note was written up as, for example sales_call, standup, minutes, one_on_one |
summary_text | string | The summary, plain text |
summary_html | string | The summary as simple HTML (paragraphs, lists, bold) |
share_url | string | Link that opens the note in Parley Notes (for its owner) |
participant_emails | string[] | Email addresses of the people invited, lower case |
participant_names | string[] | Names of the people who spoke or were invited |
action_items | object[] | {text, owner_name, owner_email, due_date} each; due_date is YYYY-MM-DD |
decisions | string[] | Decisions taken |
next_steps | string[] | The agreed next step first (with its date when one was said), then follow-ups |
participant_emails_text | string | participant_emails joined with , |
participant_names_text | string | participant_names joined with , |
action_items_text | string | One line per item: - text (owner, email; due YYYY-MM-DD) |
decisions_text | string | One line per decision, starting - |
next_steps_text | string | One line per next step, starting - |
An owner's email is filled only when the owner matched someone on the invitation; it is never guessed.
{
"id": "meet_1761213600000123",
"title": "Acme renewal call",
"date_iso": "2026-10-20T14:00:00.000Z",
"duration_min": 30,
"occasion": "sales_call",
"summary_text": "Acme wants to renew for two years. Budget is approved; legal reviews the contract this week.",
"summary_html": "<p>Acme wants to renew for two years. Budget is approved; legal reviews the contract this week.</p>",
"share_url": "https://app.parleynotes.com/?note=Xk3pQ9vRt2LmN8wA",
"participant_emails": ["dana@acme.example", "lee@acme.example"],
"participant_names": ["Dana Reyes", "Lee Park"],
"action_items": [
{ "text": "Send the renewal quote", "owner_name": "Bruno", "owner_email": "bruno@parleynotes.com", "due_date": "2026-10-24" },
{ "text": "Share the case study", "owner_name": "Dana Reyes", "owner_email": "dana@acme.example", "due_date": "" }
],
"decisions": ["Renew for two years"],
"next_steps": ["Quote to Dana (2026-10-24)", "Check in after the legal review"],
"participant_emails_text": "dana@acme.example, lee@acme.example",
"participant_names_text": "Dana Reyes, Lee Park",
"action_items_text": "- Send the renewal quote (Bruno, bruno@parleynotes.com; due 2026-10-24)\n- Share the case study (Dana Reyes, dana@acme.example)",
"decisions_text": "- Renew for two years",
"next_steps_text": "- Quote to Dana (2026-10-24)\n- Check in after the legal review"
}
The action item row
GET /action-items and the action_item.created hook give one row per open action item:
| Field | Type | What it is |
|---|---|---|
id | string | noteId:itemId, stable for the same item |
text, owner_name, owner_email, due_date | string | As in the note object |
priority | string | low, medium, high, or "" when the conversation did not say |
note_id, note_title, note_date_iso, share_url | string | The note it came from |
{
"id": "meet_1761213600000123:ai_1761213600000_0",
"text": "Send the renewal quote",
"owner_name": "Bruno",
"owner_email": "bruno@parleynotes.com",
"due_date": "2026-10-24",
"priority": "high",
"note_id": "meet_1761213600000123",
"note_title": "Acme renewal call",
"note_date_iso": "2026-10-20T14:00:00.000Z",
"share_url": "https://app.parleynotes.com/?note=Xk3pQ9vRt2LmN8wA"
}
3. Endpoints
Lists and searches cover the account's 500 newest notes. A note still being written is not returned until its summary is ready.
GET /me
Who the token belongs to. Scope: any.
curl https://app.parleynotes.com/api/v1/me -H "Authorization: Bearer pn_at_..."
{ "id": "Zr8kq2...", "email": "dana@example.com", "name": "Dana Reyes", "plan": "regular" }
plan is free, starter, regular or power.
GET /notes
The notes, newest first. Scope: notes:read.
| Query | Meaning |
|---|---|
since | Only notes on or after this time: YYYY-MM-DD or ISO 8601 |
until | Only notes on or before this time (YYYY-MM-DD means the end of that day) |
occasion | Only notes written up as this occasion, for example sales_call |
participant_email | Only notes where this address was invited |
q | Words to look for in the title, summary and topics |
limit | 1 to 100, default 25 |
cursor | The next_cursor of the previous page |
curl "https://app.parleynotes.com/api/v1/notes?since=2026-10-01&participant_email=dana@acme.example&limit=10" \
-H "Authorization: Bearer pn_at_..."
{
"data": [ { "id": "meet_1761213600000123", "title": "Acme renewal call", "...": "every field of the note object" } ],
"next_cursor": "MjAyNi0xMC0yMFQxNDowMDowMC4wMDBaAW1lZXRfMTc2MTIxMzYwMDAwMDEyMw"
}
next_cursor is "" on the last page. Cursors are opaque; send them back unchanged.
GET /notes/find
Find notes by title, participant email or date (at least one). Answers a list, at most 10, newest first, and [] when nothing matches. Scope: notes:read.
| Query | Meaning |
|---|---|
title | Words in the title (case does not matter) |
participant_email | An address that was invited |
date | The note's day, YYYY-MM-DD |
curl "https://app.parleynotes.com/api/v1/notes/find?title=renewal" -H "Authorization: Bearer pn_at_..."
[ { "id": "meet_1761213600000123", "title": "Acme renewal call", "...": "every field of the note object" } ]
GET /notes/{id}
One note. Scope: notes:read. 404 not_found for an id that does not exist, is not this account's, is still being written, or is a health note.
curl https://app.parleynotes.com/api/v1/notes/meet_1761213600000123 -H "Authorization: Bearer pn_at_..."
Answers the note object.
POST /notes
Create a note from text or from an audio link. Scope: notes:write. Send exactly one of text and audio_url.
| Field | Type | Meaning |
|---|---|---|
text | string | A transcript, an email or any text, up to 120,000 characters |
audio_url | string | An http or https link to an audio file or a podcast episode (Apple Podcasts, Spotify, an RSS feed or a podcast page) |
title | string | Optional. Without one, Parley Notes names the note |
folder | string | Optional. The Parley Notes folder to file it in |
curl -X POST https://app.parleynotes.com/api/v1/notes \
-H "Authorization: Bearer pn_at_..." \
-H "Content-Type: application/json" \
-d '{"text": "Dana: we can sign by Friday if legal agrees.\nBruno: I will send the quote today.", "title": "Call with Acme"}'
Answers within seconds with 202 Accepted:
{ "id": "meet_1761213600000123", "status": "processing" }
Parley Notes then transcribes (for audio) and writes the summary in the background, which can take a few minutes for a long recording. When the note is ready it appears in the person's Parley Notes with this id, and the note.ready hook fires with it. Imports count against the account's limits like any import in the app: a summary uses the plan's allowance, and an audio link needs a paid plan once the account's free imports are used.
| Refusal | Answer |
|---|---|
Both or neither of text and audio_url, or a bad URL | 400 invalid_request |
text over 120,000 characters | 413 too_large |
| Audio on a free plan with no free imports left | 402 plan_required |
| The account's daily import limit, or 30 new notes in an hour | 429 daily_limit or rate_limited, with Retry-After |
If the import itself fails later (an unreachable link, no speech), no note is made and no hook fires. If the summary fails, the note is saved with its transcript and waits in the app for the person to summarize it.
GET /action-items
The open action items across the notes, one row per item, newest note first. Scope: notes:read.
| Query | Meaning |
|---|---|
since | Only items from notes on or after this time |
owner_email | Only items owned by this address |
limit, cursor | As for GET /notes |
curl "https://app.parleynotes.com/api/v1/action-items?owner_email=bruno@parleynotes.com" -H "Authorization: Bearer pn_at_..."
{ "data": [ { "id": "meet_1761213600000123:ai_1761213600000_0", "text": "Send the renewal quote", "...": "every field of the action item row" } ], "next_cursor": "" }
4. REST hooks
Instead of polling, subscribe a URL and Parley Notes POSTs to it.
| Event | When | Body |
|---|---|---|
note.ready | A note's first summary is written (not a later re-summary) | The note object |
action_item.created | The same moment, once per open action item of that note | The action item row |
Subscribe: POST /hooks
Scope: hooks. target_url must be https on a public host.
curl -X POST https://app.parleynotes.com/api/v1/hooks \
-H "Authorization: Bearer pn_at_..." \
-H "Content-Type: application/json" \
-d '{"event": "note.ready", "target_url": "https://hooks.example.com/parley"}'
201 Created:
{ "id": "hk_Q2x9...", "event": "note.ready", "target_url": "https://hooks.example.com/parley", "created_at": "2026-10-23T10:00:00.000Z" }
An account holds at most 100 subscriptions.
Unsubscribe: DELETE /hooks/{id}
Scope: hooks. Answers 200 {"id": "hk_Q2x9...", "deleted": true}, or "deleted": false when it was already gone (unsubscribing twice is not an error).
Sample data: GET /hooks/sample?event=
Scope: notes:read. The latest three, in exactly the shape the hook sends: notes for note.ready, action item rows for action_item.created. Zapier uses it to show sample data while a Zap is set up.
curl "https://app.parleynotes.com/api/v1/hooks/sample?event=action_item.created" -H "Authorization: Bearer pn_at_..."
Delivery
POSTwithContent-Type: application/jsonandUser-Agent: ParleyNotes-Hooks/1; one event per request.- Any 2xx means delivered.
- 410 Gone removes the subscription; nothing more is sent to it.
- 429 is retried after its
Retry-After. - 5xx, a timeout (10 seconds) or a network failure is retried after 2 seconds, 10 seconds and 60 seconds, then given up.
- Any other 4xx is given up at once.
- Parley Notes never sends faster than 30 requests a second or more than 10,000 in five minutes; past that, deliveries wait.
- A health note never fires a hook. When the person disconnects your app, its subscriptions stop at once.
5. Rate limits
| Limit | Value | Past it |
|---|---|---|
| Requests per connection | 120 a minute | 429 rate_limited with Retry-After (seconds) |
| New notes per account | 30 an hour | 429 rate_limited with Retry-After |
| Audio imports | the account's daily limit (shared with the app) | 429 daily_limit with Retry-After |
Wait the Retry-After seconds, then try again.
6. Errors
Every API error (outside the OAuth token endpoint, which follows OAuth's own format) is:
{ "error": { "code": "not_found", "message": "No note with that id, or it is still being written." } }
| Status | code | Meaning |
|---|---|---|
| 400 | invalid_request | A parameter is missing or not valid; message says which |
| 401 | invalid_token | No token, or it is unknown, expired or revoked. Refresh and retry. Comes with WWW-Authenticate |
| 402 | plan_required | The account's plan does not include this |
| 403 | insufficient_scope | The token is valid but was not given the scope this endpoint needs. Connect again with it |
| 404 | not_found | No such note, or no such endpoint |
| 413 | too_large | The text is too long |
| 429 | rate_limited, daily_limit, too_many_hooks | A limit is reached; see Retry-After |
| 500 | server_error | Something failed on our side. Try again |
7. Versioning
The version is in the path: /api/v1. Within v1, Parley Notes may add endpoints, query parameters, fields in responses and hook events; your code should ignore what it does not know. Removing or renaming a field, changing its type, or changing what an endpoint does means a new version (/api/v2), and v1 then keeps working for at least 12 months after v2 is announced.
Questions or a client id for your app: write to the Parley Notes team through the support page.