Search the docs
Find a page, a section, or an endpoint.
Addresses
Every addresses endpoint, generated from the API's own schemas.
List addresses
get/api/v1/identities
Alphabetical by address. `cursor` is the last address on the page before it, not a timestamp — this is the one list that is not ordered by time, because it is read by somebody looking for a particular address.
Parameters
- limitnumber
- cursorstring
- tenant_idstring
- pod_iduuid
Responses
- 200A page of addresses.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities \
-H "Authorization: Bearer $PIDGEON_API_KEY"Create an address
post/api/v1/identities
Idempotent with an `Idempotency-Key` header: the same key returns the first result rather than creating a second address.
Parameters
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Body
- domain_iduuid
From `GET /domains`, or the `domain_id` on any address you already have.
- domainstring
A domain name you own, or the shared one. An alternative to `domain_id`.
- addressstring
A whole address — the local part and the domain in one. An alternative to the other two.
- local_partstring
Everything before the `@`.
- display_namestring | null
The name a recipient sees beside the address.
- inboxboolean
Whether it keeps mail. With an inbox what arrives is stored and a person can open it; without one it becomes an event and your webhook fires. Defaults from `type`.
- typeenum
A label for what this address is for. It does not change what the address can do — that is `inbox`.
human · team · application · workflow · agent
- modeenum
Who answers: a person, software, or both.
human · automated · hybrid
- expires_atdate-time | null
When it stops accepting mail. Expiry suspends; it never deletes, so the correspondence survives.
- send_policyenum
Who it may write to. `reply_only` makes an address structurally unable to start a conversation with a stranger.
open · reply_only · list
- tenant_idstring | null
Your own opaque grouping — a customer id, whatever your side calls it. Never parsed here.
- pod_iduuid | null
The pod this belongs to — your account’s default when you name none. A pod is a sending boundary: mail from it lands on its own reputation, so one of your customers cannot damage another’s delivery. It does not change what an address can do, and no key is scoped to one.
Responses
- 201The address.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 402Your plan does not include this. `error.message` says what does.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409This `Idempotency-Key` was used with a different request, or that request is still running. Nothing was sent by this call.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Fetch one address
get/api/v1/identities/{id}
Parameters
- idstring
Responses
- 200The address.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id} \
-H "Authorization: Bearer $PIDGEON_API_KEY"Update an address
patch/api/v1/identities/{id}
Parameters
- idstring
Body
- display_namestring | null
The name a recipient sees beside the address.
- modeenum
Who answers: a person, software, or both.
human · automated · hybrid
- capabilitiesobject
- statusenum
active · suspended · archived
- expires_atdate-time | null
When it stops accepting mail. Expiry suspends; it never deletes, so the correspondence survives.
- send_policyenum
Who it may write to. `reply_only` makes an address structurally unable to start a conversation with a stranger.
open · reply_only · list
- tenant_idstring | null
Your own opaque grouping — a customer id, whatever your side calls it. Never parsed here.
- pod_iduuid | null
The pod this belongs to — your account’s default when you name none. A pod is a sending boundary: mail from it lands on its own reputation, so one of your customers cannot damage another’s delivery. It does not change what an address can do, and no key is scoped to one.
Responses
- 200The address.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id} \
-X PATCH \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Delete an address
delete/api/v1/identities/{id}
Takes its messages, routing rules and webhooks with it.
Parameters
- idstring
Responses
- 200Deleted.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id} \
-X DELETE \
-H "Authorization: Bearer $PIDGEON_API_KEY"Who can reach this address
get/api/v1/identities/{id}/access
Reading the list requires being able to reach the address: who has access to somebody’s mailbox is itself private.
Parameters
- idstring
Responses
- 200The access list.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/access \
-H "Authorization: Bearer $PIDGEON_API_KEY"What this address may write to
get/api/v1/identities/{id}/send-rules
The allow and block list, and the `send_policy` it hangs off — the rules mean nothing without it. Requires `send` on the address rather than `admin`: a caller refused a recipient has to be able to find out why.
Parameters
- idstring
Responses
- 200The list.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/send-rules \
-H "Authorization: Bearer $PIDGEON_API_KEY"Who may write to this address
get/api/v1/identities/{id}/receive-rules
The rules that decide what reaches this address. Requires `read` rather than `admin`: anybody who may read the mail may see why some of it did not arrive. **Workspace-wide rules are not listed here** — these are the address’s own, and a key that administers one address must not be able to silence a sender for every other address in the workspace.
Parameters
- idstring
Responses
- 200The list.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/receive-rules \
-H "Authorization: Bearer $PIDGEON_API_KEY"A mailbox’s calendar
get/api/v1/identities/{id}/calendar
Every mailbox has exactly one, from the moment it exists, so there is nothing to create. `created_at` is the mailbox’s, because the calendar is the mailbox.
Parameters
- idstring
Responses
- 200The calendar.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/calendar \
-H "Authorization: Bearer $PIDGEON_API_KEY"Grant access
post/api/v1/identities/{id}/access
Name exactly one of `user_id`, `api_key_id` or `everyone`. Granting **replaces** any grant that principal already had, so calling this twice changes the rung rather than leaving two answers behind. Answers 404 rather than 403 when you may not administer the address — including when you can read it.
Parameters
- idstring
Body
- user_iduuid
- api_key_iduuid
- everyoneboolean
- accessenum
What a key may do. Give an agent the narrowest one that works.
read · send · admin
Responses
- 200The access list.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/access \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Allow or block a recipient
post/api/v1/identities/{id}/send-rules
`value` is an address or a domain and the string says which: `billing@acme.com` matches one person, `acme.com` matches everybody there. **The more specific rule wins**, so blocking a domain and allowing one person at it does what it looks like. Adding **replaces** any rule already there for that value, so `allow` where `block` sat is a change of mind rather than a conflict. A `block` refuses under every policy, including `open`.
Parameters
- idstring
Body
- effectenum
allow · block
- valuestring
Responses
- 200The list.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/send-rules \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Decide what happens to a sender
post/api/v1/identities/{id}/receive-rules
`value` is an address or a domain and the string says which: `billing@acme.com` matches one person, `acme.com` matches everybody there. **The more specific rule wins**, and an address’s own rule wins over a workspace-wide one. Adding **replaces** any rule already there for that value. `action` is `reject`, `spam`, `trash`, `archive` or `inbox`. **`reject` refuses before the message is stored** — no row, no thread, no storage charged and no webhook — which is what an address nobody will ever open wants. The other four file it, and `inbox` means *never classify this sender as spam*. **It is not a security control.** The `From:` header is written by the sender, and anybody blocked can change it for the price of a new address. What this buys is not paying for mail from senders who have not bothered.
Parameters
- idstring
Body
- actionenum
reject · spam · trash · archive · inbox
- valuestring
Responses
- 200The list.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/receive-rules \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Set the default time zone
patch/api/v1/identities/{id}/calendar
The zone an event created without one gets. **Changing it does not move existing events**: an event stores the wall time it was given, so "every Tuesday at 09:00" is still 09:00 afterwards.
Parameters
- idstring
- If-Matchstring
The `etag` you last read. A stale one is a `409` naming the current version — two agents on one mailbox is the case this product is for, and last-write-wins loses a meeting.
Body
- timezonestring
An IANA name, like `Europe/Lisbon`. The times above are read in it.
Responses
- 200The calendar.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/calendar \
-X PATCH \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Revoke access
delete/api/v1/identities/{id}/access
By the grant’s own id, which `GET` returned as `id`.
Parameters
- idstring
- grant_iduuid
Responses
- 200Revoked.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/access \
-X DELETE \
-H "Authorization: Bearer $PIDGEON_API_KEY"Remove a rule
delete/api/v1/identities/{id}/send-rules
By the rule’s own id, which `GET` returned as `id`.
Parameters
- idstring
- rule_iduuid
Responses
- 200Removed.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/send-rules \
-X DELETE \
-H "Authorization: Bearer $PIDGEON_API_KEY"Remove a rule
delete/api/v1/identities/{id}/receive-rules
By the rule’s own id, which `GET` returned as `id`.
Parameters
- idstring
- rule_iduuid
Responses
- 200Removed.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/receive-rules \
-X DELETE \
-H "Authorization: Bearer $PIDGEON_API_KEY"Everything on the calendar
get/api/v1/identities/{id}/calendar/events
Ordered by when it last changed. `GET /agenda` is the other read and the one you usually want: this answers *what is on this calendar*, that answers *what is on between these two moments*.
Parameters
- idstring
- limitnumber
- cursorstring
Responses
- 200The events.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/calendar/events \
-H "Authorization: Bearer $PIDGEON_API_KEY"What is on, in a window, in order
get/api/v1/identities/{id}/calendar/agenda
`include_overlapping` defaults to true, which is the difference between *what starts today* and *what is on today*: a meeting that began last night and runs into this morning is on today, and a naive `starts_at between` misses it. Cancelled events are left out — an agenda is what you are doing.
Parameters
- idstring
- afterdate-time
- beforedate-time
- include_overlappingenum
true · false
- limitnumber
Responses
- 200The agenda.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/calendar/agenda \
-H "Authorization: Bearer $PIDGEON_API_KEY"Put something on the calendar
post/api/v1/identities/{id}/calendar/events
`start` and `end` are **local wall times with no zone on them** — `2026-10-20T14:00:00` — and `timezone` is a field of its own. That pair is the truth; `starts_at` is the same moment as an instant, derived from it, and it is what survives a daylight-saving change. For an all-day event, `end` is the last day, inclusive.
Parameters
- idstring
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Body
- titlestring
- startstring
- endstring
- timezonestring | null
An IANA name, like `Europe/Lisbon`. The times above are read in it.
- all_dayboolean
- descriptionstring | null
- locationstring | null
- statusenum
confirmed · tentative · cancelled
- metadataobject
- attendeesobject[]
- send_invitesboolean
- recurrenceobject | null
Responses
- 201The event.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused, and nothing was sent to anybody. `error.code` says why: `blocked` (a rule), `not_permitted` (the send policy), `abuse_blocked` (a sending limit) or `forbidden` (the address is not one this key can send from, or is not active).
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409This `Idempotency-Key` was used with a different request, or that request is still running. Nothing was sent by this call.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/calendar/events \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'One event
get/api/v1/identities/{id}/calendar/events/{eventId}
Parameters
- idstring
- eventIdstring
Responses
- 200The event.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 410It was here and is not any more. Forget the id.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/calendar/events/{eventId} \
-H "Authorization: Bearer $PIDGEON_API_KEY"Change an event
patch/api/v1/identities/{id}/calendar/events/{eventId}
An omitted field survives. `sequence` rises only when the change is one attendees see — the time, the title, the place, the status, the list. A `metadata` edit moves the `etag` and not the sequence, because nobody is ever told about it.
Parameters
- idstring
- eventIdstring
- modeenum
Which dates of a series this is about. Name one **date** of a series — the id `listEventInstances` gives you — and say whether you mean that date (`single`, the default) or that date onwards (`future`, which splits the series). Naming the series means the whole thing, which is why there is no `all`.
single · future
- If-Matchstring
The `etag` you last read. A stale one is a `409` naming the current version — two agents on one mailbox is the case this product is for, and last-write-wins loses a meeting.
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Body
- titlestring
- startstring
- endstring
- timezonestring
An IANA name, like `Europe/Lisbon`. The times above are read in it.
- all_dayboolean
- descriptionstring | null
- locationstring | null
- statusenum
confirmed · tentative · cancelled
- metadataobject
- attendeesobject[]
- recurrenceobject | null
- send_invitesboolean
Responses
- 200The event.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused, and nothing was sent to anybody. `error.code` says why: `blocked` (a rule), `not_permitted` (the send policy), `abuse_blocked` (a sending limit) or `forbidden` (the address is not one this key can send from, or is not active).
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409This `Idempotency-Key` was used with a different request, or that request is still running. Nothing was sent by this call.
- 410It was here and is not any more. Forget the id.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/calendar/events/{eventId} \
-X PATCH \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'Take it off the calendar
delete/api/v1/identities/{id}/calendar/events/{eventId}
On one date of a series this records that the date does not happen rather than removing a row — the rule still generates it, so a removal would bring it straight back. `mode=future` ends the series there and leaves every earlier date alone.
Parameters
- idstring
- eventIdstring
- modeenum
Which dates of a series this is about. Name one **date** of a series — the id `listEventInstances` gives you — and say whether you mean that date (`single`, the default) or that date onwards (`future`, which splits the series). Naming the series means the whole thing, which is why there is no `all`.
single · future
- If-Matchstring
The `etag` you last read. A stale one is a `409` naming the current version — two agents on one mailbox is the case this product is for, and last-write-wins loses a meeting.
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Responses
- 200Deleted.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused, and nothing was sent to anybody. `error.code` says why: `blocked` (a rule), `not_permitted` (the send policy), `abuse_blocked` (a sending limit) or `forbidden` (the address is not one this key can send from, or is not active).
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409This `Idempotency-Key` was used with a different request, or that request is still running. Nothing was sent by this call.
- 410It was here and is not any more. Forget the id.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/calendar/events/{eventId} \
-X DELETE \
-H "Authorization: Bearer $PIDGEON_API_KEY"The dates a series actually happens
get/api/v1/identities/{id}/calendar/events/{eventId}/instances
Most of them have no row — they are expanded from the rule, which is what makes a weekly meeting cheap to store. An id here is `{seriesId}::{originalStart}`, derived rather than allocated, and it does **not** change when you edit that date. A date somebody edited comes back in place of the one the rule generated, under that id.
Parameters
- idstring
- eventIdstring
- afterdate-time
- beforedate-time
- limitnumber
Responses
- 200The dates.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused. `error.code` says why: `forbidden` when this key may not do this or the account is on hold, `abuse_blocked` when a limit says no.
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409It conflicts with what is there: it already exists, or the `etag` you sent is not the current one.
- 410It was here and is not any more. Forget the id.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/calendar/events/{eventId}/instances \
-H "Authorization: Bearer $PIDGEON_API_KEY"Answer a meeting from the calendar
post/api/v1/identities/{id}/calendar/events/{eventId}/respond
The same act as `POST /messages/{id}/invitation/respond`, reached from the other end — an agent working from an agenda holds an event id and not a message id. When the meeting came from mail and `send_reply` is true (the default), an iCalendar `REPLY` goes to the organiser and the answer is written onto the event. When it did not — a meeting this mailbox created itself — there is nobody to reply to, so the answer is recorded and `reply_sent` is false. One of this mailbox’s own addresses must be on the attendee list, including an address routed to it.
Parameters
- idstring
- eventIdstring
- If-Matchstring
The `etag` you last read. A stale one is a `409` naming the current version — two agents on one mailbox is the case this product is for, and last-write-wins loses a meeting.
- Idempotency-Keystring
Any string unique to this attempt, reused on its retries. A repeat with the same key and the same request returns the first answer with `Idempotent-Replay: true` and does nothing — no second email. The same key with a different request is a `409`. Kept for 24 hours; a failure is not kept, so a retry after one runs.
Body
- statusenum
accepted · declined · tentative
- commentstring
- send_replyboolean
Responses
- 200Answered.
- 400The request is not valid. `error.field` names the input at fault when there is one.
- 401No API key, or one that is not valid.
- 403Refused, and nothing was sent to anybody. `error.code` says why: `blocked` (a rule), `not_permitted` (the send policy), `abuse_blocked` (a sending limit) or `forbidden` (the address is not one this key can send from, or is not active).
- 404No such resource, or one this key cannot reach. The two answer the same, so treat a 404 as “not available to you” rather than “does not exist”.
- 409This `Idempotency-Key` was used with a different request, or that request is still running. Nothing was sent by this call.
- 410It was here and is not any more. Forget the id.
- 429Rate limited. `X-RateLimit-Reset` says when to retry.
- 500Something went wrong on our side. Retry; if it keeps happening, tell us.
- 503Something this needs is not answering right now. Retry in a moment.
curl https://pidgeon.ai/api/v1/identities/{id}/calendar/events/{eventId}/respond \
-X POST \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'