0.7.3 or later.
Inbound means from the external party to the agent. Outbound means from the agent to that party. Apply these choices independently within each direction:
- An active exact-address
alloworblockchoice takes precedence in either mode. - Without an exact choice, email uses matching
allowdomains in whitelist mode orblockdomains in blacklist mode. - Unmatched addresses are denied in whitelist mode and allowed in blacklist mode.
Contact visibility
Email and phone visibility is all-or-nothing for each contact and identity. A visible group exposes all of that contact’s identifiers of that kind, including in search, lookup, vCard export, and contact context. Permission to communicate remains independent for each address. A fully hidden contact returns404 by ID and is absent from listings.
Choose group visibility separately from each address’s communication permission:
Without an explicit group-visibility choice, an address permitted inbound or outbound makes that group visible. Existing explicit visibility and Profile restrictions take precedence. Existing mixed communication choices stay mixed, but the agent can see every address in a visible group. Seeing an address does not authorize sending to it. A receive-only address can contribute to visibility without being sendable. Contacts with no identifiers retain their Profile-based visibility.
Profile is the parent permission for the contact. When Profile is hidden, Email, Phone, and Memories are also hidden, and no saved address is contactable through this contact. When Profile is visible, you can independently hide Memories or make Email and Phone view-only. Existing authorized conversation history is not erased.
Hosted voice in YOLO mode can read all contacts, fields, and memories in its organization regardless of these settings. Ordinary identity-scoped API, SDK, CLI, MCP, and webhook access still follows the contact policy. Setting an identity’s default voice mode to YOLO does not change those ordinary reads.
Read and update contact access
visible covers the whole email or phone group. The legacy contactable list means can send and matches outbound_contactable. inbound_contactable lists addresses the agent may receive from. A receive-only address is absent from legacy contactable. In this example, the agent can see all phone numbers but cannot communicate with them.
Split permissions can include an optional top-level notices list and the Inkbox-Notices header.
- PATCH preserves omitted fields. An empty PATCH or an empty group object changes nothing on its own.
- A supplied legacy
contactablelist replaces both directions for that group’s current addresses. Listed addresses become explicit allows; all other current addresses become explicit blocks. An empty list blocks all current addresses in both directions. - A directional list replaces only that side, preserving the opposite side atomically. An empty directional list blocks all current addresses on that side only. Do not mix
contactablewith either directional list for the same group (422). Both directional lists may appear together. - A nonempty legacy or directional list makes its group visible when
visibleis omitted. An empty list preserves previous visibility whenvisibleis omitted. visible: falsehides the group and blocks all current addresses in both directions. Addresses subsequently added to an explicitly hidden group are also blocked both ways.- An explicit standalone exact-address allow can reveal a uniquely matched saved contact’s group while Profile is enabled, without allowing its other addresses.
profile: falsehides Email, Phone, and Memories and clears the contactable lists. A request that enables any child permission while the resulting Profile permission is false returns422.- Choices persist when the identity’s whitelist/blacklist mode changes. No revision or previous-action field is required; concurrent edits to different fields preserve each other.
visible: false with any nonempty communication list return 422. An address outside the contact returns 400; an unavailable agent/contact returns 404; insufficient authority returns 403.
Use SDK/CLI 0.6.12 or later with admin credentials for local validation of Profile-child contradictions:
{"email":{"visible":true,"contactable":[]}}. The existing contacts.access.list SDK method and contacts access list CLI command return read-only compatibility metadata, not these selected-agent access settings.
Configure receive-only access
Use SDK/CLI0.7.3 or later. The address must already belong to the contact. These examples permit receiving from ada@example.com and deny sending to every current email address on that contact:
access.json contains:
inbound_contactable and outbound_contactable instead. Rust directional reads use contacts().access().get_with_options(handle, contact_id); legacy get and update remain available with their original structs.
Read and update boolean permissions
inbound_emails, outbound_emails, inbound_phones, and outbound_phones maps. Each changes only its named direction for the listed addresses. Unlike access-list replacement, an empty map leaves all addresses unchanged. Do not combine a legacy map with a directional map for the same channel (422). The response’s legacy maps match the outbound maps. Split results can include response notices.
Each address map accepts up to 50 entries. Use JSON booleans, not strings, numbers, or null. Email keys are normalized and phone keys must be E.164. Duplicate normalized keys are rejected.
Successful PATCH returns the same effective-access shape as GET. No revision or previous-action field is required. Concurrent edits to separate fields preserve each other; the later edit to the same field wins. Other agents’ settings remain unchanged.
Use SDK/CLI
0.6.12 or later for local validation of Profile-child contradictions:
{"emails":{"ada@example.com":true},"profile":true,"memories":false}. To save initial choices with a new contact, use create contact with permissions.
Advanced policies
Use this interface when you need inherited visibility settings or guarded exact-address edits:contact_id, revision, identity_id, addresses, effective_visibility, and visibility. Each address has kind, value, nullable label, and the following choices. Without the optional identity_id query, addresses is empty and identity_id and effective_visibility are null.
Read the current policy, then supply its revision and each edited address’s observed action:
allow and block set explicit exact-address choices. inherit removes that exact choice so domain rules and the channel mode determine communication access. There are no contact-wide Email or Phone communication defaults.
Address edits default to direction: "both"; an explicit inbound or outbound edit preserves the other side. Repeating an address for disjoint directional edits is valid in the same atomic request. Overlapping edits are invalid.
expected_action can guard a one-direction edit. To change both directions when the observed actions differ, supply both expected_inbound_action and expected_outbound_action. A single legacy projected action cannot guard unseen split state; that request returns 409. Guards use allow, block, or inherit; omit unused guards rather than sending null. Use the pair for bidirectional edits and expected_action for one-direction edits in SDK and CLI examples:
visibility object contains defaults: {profile, memories} and identities: [{identity_id, profile, memories}]. Decisions are allow, block, or inherit. An identity override inherits its contact default. When both Profile decisions inherit, Profile is enabled. A blocked effective Profile requires blocked Memories and prevents Email or Phone address allows for the selected identity.
Omitting visibility preserves all Profile/Memories settings. Supplying it replaces that entire portion, so retain other identities’ settings. visibility: null, unknown fields, overlapping address edits, and invalid decisions return 422. Up to 500 address edits and 500 distinct identities are accepted. Stale revisions or changed observed actions return 409 without saving changes.
Replacing Profile/Memories settings preserves email/phone visibility while Profile stays enabled. Blocking Profile makes those visibility settings ineffective and prevents an exact-address allow from revealing contact data.
Python uses communication_policy.get(contact_id, identity_id) and .replace(..., expected_revision=..., identity_id=..., addresses=[ContactAddressUpdate(...)]). TypeScript uses communicationPolicy.get(contactId, identityId) and .replace(contactId, {expectedRevision, identityId, addresses}), with expectedAction on address edits. Rust uses communication_policy().get_for_identity(contact_id, identity_id) and replace with ReplaceContactCommunicationPolicy; replace_with_visibility also replaces visibility.
The CLI provides contacts communication-policy get CONTACT_ID --identity-id IDENTITY_ID and set CONTACT_ID --file policy.json. Its file uses the TypeScript names expectedRevision, identityId, and expectedAction, including identityId within optional visibility overrides.
For directional edits, Python ContactAddressUpdate uses direction, expected_inbound_action, and expected_outbound_action. TypeScript and CLI files use direction, expectedInboundAction, and expectedOutboundAction. Rust adds DirectionalContactAddressUpdate, ReplaceDirectionalContactCommunicationPolicy, and communication_policy().replace_with_options; existing replace methods keep their original types.
Preview an identity’s saved view
contact: null when that view cannot see the contact. Otherwise contact includes the permitted fields, memory_count, and latest_memory. Denied memories produce zero and null without revealing their existence.
visibility.profile and visibility.memories report access even when a group has no stored content. Memories is always false when Profile is false. Existing email and phone indicators mean their visible identifier arrays are nonempty. full_profile remains a conservative legacy aggregate; use the individual visibility fields for new interfaces. The identity listing returns items, limit, offset, and has_more; an agent can list only its own view.
inbound_email, outbound_email, inbound_phone, and outbound_phone report whether any visible address of that kind permits communication in the named direction. These booleans do not grant permission for every address. Use per-address permissions for a particular recipient.
Use inkbox.contacts.communication_policy.preview(contact_id, identity_id) in Python or inkbox.contacts.communicationPolicy.preview(contactId, identityId) in TypeScript. The CLI provides contacts communication-policy preview CONTACT_ID IDENTITY_ID and contacts communication-policy list HANDLE.
Manage an identity’s contact permissions
The response contains
items, limit, offset, and has_more. Each item has:
Effective communication access includes standalone address, domain, and number
rules.
no_identifiers means no identifiers are stored, not that access is
denied. Resetting a policy to inherited values does not reset its revision.
Other identities’ overrides are not included in this roster. Boolean permission updates preserve their settings automatically.
Phone compatibility
Existing phone and iMessage rule methods remain supported.phone_filter_mode and imessage_filter_mode are shared aliases: writing either sets both directional modes. Conflicting values return 422. Use phone_inbound_filter_mode and phone_outbound_filter_mode for independent defaults. Shared reads report the common effective mode when both agree, otherwise the last shared baseline. See identity mode semantics.
A visible receive-only contact is not necessarily eligible for shared-line iMessage setup, which requires both effective permissions and grants neither. Dedicated traffic checks its own direction. An inbound message never guarantees that an outbound reply is permitted.
Identity-level permission configuration does not require a dedicated phone number. Older SDK helpers may still check for one locally; use their existing iMessage methods or upgrade. Releasing or replacing a phone number preserves the identity’s permissions, and enabling another transport does not reset them.
Contact edits
Changing the identifiers of an existing contact requires admin credentials or the Inkbox Console. Added identifiers follow their exact-address rules, email domain rules, and channel mode, except that an explicitly hidden group keeps added identifiers blocked. Humans and admin API keys can remove identifiers or delete a contact without resetting Profile or Memories settings. Exact-address rules survive contact or identifier deletion. Agent profile edits cannot grant communication permissions. Agent profile PATCH requests must omitemails and phones, even when their values are unchanged. Agent credentials cannot delete contacts with configured Profile or Memories restrictions; use an admin API key or the Inkbox Console. Merging contacts with different permission settings returns 409 with detail.error: "contact_policy_mismatch" and a readable detail.message.
MCP users manage communication rules in the authenticated Inkbox Console. The agent can inspect its permitted contact view but cannot change its own rules.
Webhook replays refresh contact names and memories against current permissions. A visible name can accompany an empty memory list, but memories are never included without Profile access. Archived contact-scoped conversation context is omitted when it cannot be refreshed; the original event and directly scoped conversation history remain available.
