‹ API documentation
API documentation · v1

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/json with 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

ScopeAllows
notes:readGET /me, GET /notes, GET /notes/find, GET /notes/{id}, GET /action-items, GET /hooks/sample
notes:writePOST /notes
hooksPOST /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.

SituationAnswer
Access token missing, unknown, expired or revoked401 invalid_token, with WWW-Authenticate: Bearer ... (never 403)
Refresh token unknown, expired, reused or revoked400 {"error": "invalid_grant"}
Code unknown, used, expired, or another redirect_uri400 {"error": "invalid_grant"}
Wrong client id or secret at the token endpoint401 {"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.

FieldTypeWhat it is
idstringThe note's id
titlestringThe note's title
date_isostringWhen the note was recorded or made, ISO 8601 in UTC
duration_minintegerLength in minutes (0 for a note that was not spoken)
occasionstringWhat the note was written up as, for example sales_call, standup, minutes, one_on_one
summary_textstringThe summary, plain text
summary_htmlstringThe summary as simple HTML (paragraphs, lists, bold)
share_urlstringLink that opens the note in Parley Notes (for its owner)
participant_emailsstring[]Email addresses of the people invited, lower case
participant_namesstring[]Names of the people who spoke or were invited
action_itemsobject[]{text, owner_name, owner_email, due_date} each; due_date is YYYY-MM-DD
decisionsstring[]Decisions taken
next_stepsstring[]The agreed next step first (with its date when one was said), then follow-ups
participant_emails_textstringparticipant_emails joined with ,
participant_names_textstringparticipant_names joined with ,
action_items_textstringOne line per item: - text (owner, email; due YYYY-MM-DD)
decisions_textstringOne line per decision, starting -
next_steps_textstringOne 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:

FieldTypeWhat it is
idstringnoteId:itemId, stable for the same item
text, owner_name, owner_email, due_datestringAs in the note object
prioritystringlow, medium, high, or "" when the conversation did not say
note_id, note_title, note_date_iso, share_urlstringThe 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.

QueryMeaning
sinceOnly notes on or after this time: YYYY-MM-DD or ISO 8601
untilOnly notes on or before this time (YYYY-MM-DD means the end of that day)
occasionOnly notes written up as this occasion, for example sales_call
participant_emailOnly notes where this address was invited
qWords to look for in the title, summary and topics
limit1 to 100, default 25
cursorThe 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.

QueryMeaning
titleWords in the title (case does not matter)
participant_emailAn address that was invited
dateThe 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.

FieldTypeMeaning
textstringA transcript, an email or any text, up to 120,000 characters
audio_urlstringAn http or https link to an audio file or a podcast episode (Apple Podcasts, Spotify, an RSS feed or a podcast page)
titlestringOptional. Without one, Parley Notes names the note
folderstringOptional. 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.

RefusalAnswer
Both or neither of text and audio_url, or a bad URL400 invalid_request
text over 120,000 characters413 too_large
Audio on a free plan with no free imports left402 plan_required
The account's daily import limit, or 30 new notes in an hour429 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.

QueryMeaning
sinceOnly items from notes on or after this time
owner_emailOnly items owned by this address
limit, cursorAs 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.

EventWhenBody
note.readyA note's first summary is written (not a later re-summary)The note object
action_item.createdThe same moment, once per open action item of that noteThe 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

  • POST with Content-Type: application/json and User-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

LimitValuePast it
Requests per connection120 a minute429 rate_limited with Retry-After (seconds)
New notes per account30 an hour429 rate_limited with Retry-After
Audio importsthe 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." } }
StatuscodeMeaning
400invalid_requestA parameter is missing or not valid; message says which
401invalid_tokenNo token, or it is unknown, expired or revoked. Refresh and retry. Comes with WWW-Authenticate
402plan_requiredThe account's plan does not include this
403insufficient_scopeThe token is valid but was not given the scope this endpoint needs. Connect again with it
404not_foundNo such note, or no such endpoint
413too_largeThe text is too long
429rate_limited, daily_limit, too_many_hooksA limit is reached; see Retry-After
500server_errorSomething 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.