Skip to content

Latest commit

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..

README.md

inkbox

Python SDK for the Inkbox API — API-first communication infrastructure for AI agents (email, phone, identities).

Install

pip install inkbox

Requires Python ≥ 3.11.

Authentication

You'll need an API key to use this SDK. Get one at console.inkbox.ai.

Quick start

importosfrominkboximportInkboxwithInkbox(api_key=os.environ["INKBOX_API_KEY"]) asinkbox:
# Create an agent identityidentity=inkbox.create_identity("support-bot")
# Create and link new channelsidentity.create_mailbox(display_name="Support Bot")
identity.provision_phone_number(type="toll_free")
# Send email directly from the identityidentity.send_email(
to=["customer@example.com"],
subject="Your order has shipped",
body_text="Tracking number: 1Z999AA10123456784",
)
# Place an outbound callidentity.place_call(
to_number="+18005559999",
client_websocket_url="wss://my-app.com/voice",
)
# Read inboxformessageinidentity.iter_emails():
print(message.subject)
# List callscalls=identity.list_calls()

Authentication

ArgumentTypeDefaultDescription
api_keystrrequiredYour ApiKey_... token
base_urlstrAPI defaultOverride for self-hosting or testing
timeoutfloat30.0Request timeout in seconds

Use with Inkbox(...) as inkbox: (recommended) or call inkbox.close() manually to clean up HTTP connections.


Identities

inkbox.create_identity() and inkbox.get_identity() return an AgentIdentity object that holds the identity's channels and exposes convenience methods scoped to those channels.

# Create and fully provision an identityidentity=inkbox.create_identity("sales-bot")
mailbox=identity.create_mailbox(display_name="Sales Bot") # creates + linksphone=identity.provision_phone_number(type="toll_free") # provisions + linksprint(mailbox.email_address)
print(phone.number)
# Link an existing mailbox or phone number instead of creating new onesidentity.assign_mailbox("mailbox-uuid-here")
identity.assign_phone_number("phone-number-uuid-here")
# Get an existing identityidentity=inkbox.get_identity("sales-bot")
identity.refresh() # re-fetch channels from API# List all identities for your orgall_identities=inkbox.list_identities()
# Update status or handleidentity.update(status="paused")
identity.update(new_handle="sales-bot-v2")
# Unlink channels (without deleting them)identity.unlink_mailbox()
identity.unlink_phone_number()
# Deleteidentity.delete()

Mail

# Send an email (plain text and/or HTML)sent=identity.send_email(
to=["user@example.com"],
subject="Hello from Inkbox",
body_text="Hi there!",
body_html="<p>Hi there!</p>",
cc=["manager@example.com"],
bcc=["archive@example.com"],
)
# Send a threaded replyidentity.send_email(
to=["user@example.com"],
subject=f"Re: {sent.subject}",
body_text="Following up!",
in_reply_to_message_id=sent.id,
)
# Send with attachmentsidentity.send_email(
to=["user@example.com"],
subject="See attached",
body_text="Please find the file attached.",
attachments=[{
"filename": "report.pdf",
"content_type": "application/pdf",
"content_base64": "<base64-encoded-content>",
}],
)
# Iterate inbox (paginated automatically)formsginidentity.iter_emails():
print(msg.subject, msg.from_address, msg.is_read)
# Filter by direction: "inbound" or "outbound"formsginidentity.iter_emails(direction="inbound"):
print(msg.subject)
# Iterate only unread emailsformsginidentity.iter_unread_emails():
print(msg.subject)
# Mark messages as readidentity.mark_emails_read([msg.idformsginidentity.iter_unread_emails()])
# Get all emails in a thread (thread_id comes from msg.thread_id)thread=identity.get_thread(msg.thread_id)
forminthread.messages:
print(m.subject, m.from_address)

Phone

# Place an outbound call — stream audio over WebSocketcall=identity.place_call(
to_number="+15167251294",
client_websocket_url="wss://your-agent.example.com/ws",
)
print(call.status, call.rate_limit.calls_remaining)
# Or receive call events via webhook insteadcall=identity.place_call(
to_number="+15167251294",
webhook_url="https://your-agent.example.com/call-events",
)
# List calls (paginated)calls=identity.list_calls(limit=10, offset=0)
forcallincalls:
print(call.id, call.direction, call.remote_phone_number, call.status)
# Fetch transcript segments for a callsegments=identity.list_transcripts(calls[0].id)
fortinsegments:
print(f"[{t.party}] {t.text}") # party: "local" or "remote"# Read transcripts across all recent callsforcallinidentity.list_calls(limit=10):
segments=identity.list_transcripts(call.id)
ifnotsegments:
continueprint(f"\n--- Call {call.id} ({call.direction}) ---")
fortinsegments:
print(f" [{t.party:6}] {t.text}")
# Filter to only the remote party's speechfortinidentity.list_transcripts(calls[0].id):
ift.party=="remote":
print(t.text)
# Search transcripts across a phone number (org-level)hits=inkbox.phone_numbers.search_transcripts(phone.id, q="refund", party="remote")
fortinhits:
print(f"[{t.party}] {t.text}")

Org-level Mailboxes

Manage mailboxes directly without going through an identity. Access via inkbox.mailboxes.

# List all mailboxes in the organisationmailboxes=inkbox.mailboxes.list()
# Get a specific mailboxmailbox=inkbox.mailboxes.get("abc-xyz@inkboxmail.com")
# Create a mailbox linked to an agent identitymailbox=inkbox.mailboxes.create(agent_handle="support-agent", display_name="Support Inbox")
print(mailbox.email_address)
# Update display name or webhook URLinkbox.mailboxes.update(mailbox.email_address, display_name="New Name")
inkbox.mailboxes.update(mailbox.email_address, webhook_url="https://example.com/hook")
inkbox.mailboxes.update(mailbox.email_address, webhook_url=None) # remove webhook# Full-text search across messages in a mailboxresults=inkbox.mailboxes.search(mailbox.email_address, q="invoice", limit=20)
formsginresults:
print(msg.subject, msg.from_address)
# Delete a mailboxinkbox.mailboxes.delete(mailbox.email_address)

Org-level Phone Numbers

Manage phone numbers directly without going through an identity. Access via inkbox.phone_numbers.

# List all phone numbers in the organisationnumbers=inkbox.phone_numbers.list()
# Get a specific phone number by IDnumber=inkbox.phone_numbers.get("phone-number-uuid")
# Provision a new numbernumber=inkbox.phone_numbers.provision(type="toll_free")
local=inkbox.phone_numbers.provision(type="local", state="NY")
# Update incoming call behaviourinkbox.phone_numbers.update(
number.id,
incoming_call_action="webhook",
incoming_call_webhook_url="https://example.com/calls",
)
inkbox.phone_numbers.update(
number.id,
incoming_call_action="auto_accept",
client_websocket_url="wss://example.com/ws",
)
# Full-text search across transcriptshits=inkbox.phone_numbers.search_transcripts(number.id, q="refund", party="remote")
fortinhits:
print(f"[{t.party}] {t.text}")
# Release a numberinkbox.phone_numbers.release(number=number.number)

Webhooks

Webhooks are configured on the mailbox or phone number resource — no separate registration step.

Mailbox webhooks

Set a URL on a mailbox to receive message.received and message.sent events.

# Set webhookinkbox.mailboxes.update("abc@inkboxmail.com", webhook_url="https://example.com/hook")
# Remove webhookinkbox.mailboxes.update("abc@inkboxmail.com", webhook_url=None)

Phone webhooks

Set an incoming call webhook URL and action on a phone number.

# Route incoming calls to a webhookinkbox.phone_numbers.update(
number.id,
incoming_call_action="webhook",
incoming_call_webhook_url="https://example.com/calls",
)

You can also supply a per-call webhook URL when placing a call:

identity.place_call(to_number="+15005550006", webhook_url="https://example.com/call-events")

Signing Keys

# Create or rotate the org-level webhook signing key (plaintext returned once)key=inkbox.create_signing_key()
print(key.signing_key) # save this immediately

Verifying Webhook Signatures

Use verify_webhook to confirm that an incoming request was sent by Inkbox.

frominkboximportverify_webhook# FastAPI@app.post("/hooks/mail")asyncdefmail_hook(request: Request):
raw_body=awaitrequest.body()
ifnotverify_webhook(
payload=raw_body,
headers=request.headers,
secret="whsec_...",
):
raiseHTTPException(status_code=403)
...
# Flask@app.post("/hooks/mail")defmail_hook():
raw_body=request.get_data()
ifnotverify_webhook(
payload=raw_body,
headers=request.headers,
secret="whsec_...",
):
abort(403)
...

Examples

Runnable example scripts are available in the examples/python directory:

ScriptWhat it demonstrates
register_agent_identity.pyCreate an identity, assign mailbox + phone number
agent_send_email.pySend an email and a threaded reply
read_agent_messages.pyList messages and threads
create_agent_mailbox.pyCreate, update, search, and delete a mailbox
create_agent_phone_number.pyProvision, update, and release a number
list_agent_phone_numbers.pyList all phone numbers in the org
read_agent_calls.pyList calls and print transcripts
receive_agent_email_webhook.pyRegister and delete a mailbox webhook
receive_agent_call_webhook.pyRegister, update, and delete a phone webhook

License

MIT