inkbox-ai/inkbox/inkbox-ts

Claude Code and agent skill by inkbox-ai/inkbox

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Use when writing TypeScript or JavaScript code that imports from `@inkbox/sdk`, uses `npm install @inkbox/sdk`, or when adding email, mailbox imports, phone, text/SMS, iMessage, A2A task/message history, contacts, notes, contact rules, vault, tunnels, mailbox storage, mail clients (IMAP/SMTP), or agent identity features using the Inkbox TypeScript SDK.

Install

npx vskill@latest install inkbox-ai/inkbox/inkbox-ts

SKILL.md

Inkbox TypeScript SDK

API-first communication infrastructure for AI agents — email, phone, encrypted vault, and identities.

Install & Init

bash
npm install @inkbox/sdk

Requires Node.js ≥ 22. ESM module — no context manager needed:

typescript
import { Inkbox } from "@inkbox/sdk";
const inkbox = new Inkbox({ apiKey: "ApiKey_..." });

Constructor options: { apiKey: string, baseUrl?: string, timeoutMs?: number }

Core Model

Inkbox (admin-only client)
├── .createIdentity(handle) → Promise<AgentIdentity>
├── .getIdentity(handle) → Promise<AgentIdentity>
├── .listIdentities() → Promise<AgentIdentitySummary[]>
├── .mailboxes → MailboxesResource
├── .phoneNumbers → PhoneNumbersResource
├── .texts → TextsResource
├── .imessages → IMessagesResource
├── .imessageContactRules → IMessageContactRulesResource
├── .mailIdentityContactRules → MailIdentityContactRulesResource (keyed by agentHandle)
├── .phoneIdentityContactRules → PhoneIdentityContactRulesResource (keyed by agentHandle)
├── .signingKeys → SigningKeysResource (per-identity: createOrRotate/getStatus)
├── .mailContactRules → MailContactRulesResource (DEPRECATED — per-mailbox)
├── .phoneContactRules → PhoneContactRulesResource (DEPRECATED — per-number)
├── .smsOptIns → SmsOptInsResource
├── .contacts → ContactsResource (.communicationPolicy, .permissions, .facts, .correspondence, .access, .vcards)
├── .notes → NotesResource (.access)
├── .vault → VaultResource
├── .whoami() → Promise<WhoamiResponse>
└── .createSigningKey() → Promise<SigningKey> (DEPRECATED — org-level; use .signingKeys)
AgentIdentity (identity-scoped helper)
├── .mailbox → IdentityMailbox | null
├── .phoneNumber → IdentityPhoneNumber | null
├── .mailFilterMode / .phoneFilterMode → FilterMode
├── .getCredentials() → Promise<Credentials> (requires vault unlocked)
├── .listMailContactRules() / .createMailContactRule(...) / .get/.update/.delete
├── .listPhoneContactRules() / .createPhoneContactRule(...) / ... (writes require admin credentials)
├── .getSigningKeyStatus() / .createSigningKey()
├── .listContactCommunicationPolicies() → Promise<ContactCommunicationPolicyPage>
├── mail methods (requires assigned mailbox)
├── phone methods (requires assigned phone number)
└── text methods (requires assigned phone number)

An identity must have a channel assigned before you can use mail/phone methods. If not assigned, an InkboxError is thrown.

Agent Signup

For the full agent self-signup flow (register, verify, check status, restrictions, and direct API examples), read the shared reference:

See: skills/inkbox-agent-self-signup/SKILL.md

TypeScript SDK methods: Inkbox.signup({...}), Inkbox.verifySignup(apiKey, {...}), Inkbox.resendSignupVerification(apiKey), Inkbox.getSignupStatus(apiKey).

Identities

Handles are fixed at creation. Change the display name to update an agent's presentation.

typescript
const identity = await inkbox.createIdentity("sales-agent");
const identity = await inkbox.getIdentity("sales-agent");
const identities = await inkbox.listIdentities(); // AgentIdentitySummary[]
await identity.update({ displayName: "Sales assistant" });
await identity.refresh(); // re-fetch from API, updates cached channels
await identity.delete(); // cascades: mailbox + tunnel + phone-number release

Channel Management

typescript
// Identity is created with a mailbox AND tunnel atomically — both are on the response
console.log(identity.emailAddress); // e.g. "sales-agent@inkboxmail.com"
console.log(identity.tunnel?.publicHost); // e.g. "sales-agent.inkboxwire.com"
// Phone numbers are still opt-in
const phone = await identity.provisionPhoneNumber({ type: "local", state: "NY" }); // local only; toll_free is rejected (422)
console.log(phone.number); // e.g. "+12125551234"
// Release the phone number (vendor + local)
await identity.releasePhoneNumber();

Mailboxes and tunnels are not separately linkable — they are 1:1 with their owning identity. Use inkbox.createIdentity() to provision both; use identity.delete() to remove both (cascade).

Mail

Import historical mail
typescript
import { MailImportFormat } from "@inkbox/sdk";
const created = await inkbox.mailboxes.imports.create(email, {
sourceFormat: MailImportFormat.AUTO,
originalAddresses: ["old@example.com"],
});
await inkbox.mailboxes.imports.upload(created.upload, file);
await inkbox.mailboxes.imports.start(email, created.job.id);
const job = await inkbox.mailboxes.imports.wait(email, created.job.id, {
pollIntervalMs: 5_000,
});

Formats: auto, mbox, eml, zip. A ZIP may hold .eml and/or .mbox files (a Gmail Takeout ZIP imports as-is); other entries, including nested archives, are ignored. wait returns all terminal states; failure/cancellation are job results, not transport errors. A timeout does not cancel. Counters are cumulative and never go backwards, so a stalled counter is a signal, not normal churn; counters may still remain unchanged while a slow message is processed, and they must not be treated as a percentage. Jobs run one at a time per organization and share overall import capacity, so a long queued stretch is normal; do not cancel and recreate. Unsafe imported content may be rejected.

Upload targets expire after 5 minutes: refreshUploadTarget(email, jobId) and upload again, or cancel the job so it does not hold the mailbox for 24 hours. Limits: 1 GiB per upload, 50 MiB per message, 100,000 messages and 20 originalAddresses per job, 65,000 entries per ZIP, 20 jobs per organization per 24 hours (MailImportQuotaExceededError.retryAfterSeconds), and one in-flight import per mailbox.

Send
typescript
const sent = await identity.sendEmail({
to: ["user@example.com"],
subject: "Hello",
bodyText: "Hi there!", // plain text (optional)
bodyHtml: "<p>Hi there!</p>", // HTML (optional)
cc: ["cc@example.com"], // optional
bcc: ["bcc@example.com"], // optional
inReplyToMessageId: sent.id, // for threaded replies
attachments: [{ // optional
filename: "report.pdf",
contentType: "application/pdf",
contentBase64: "<base64>",
}, {
filename: "chart.png", // inline image: set contentId and reference
contentType: "image/png", // it from bodyHtml as <img src="cid:chart">.
contentBase64: "<base64>", // needs bodyHtml + image/*, unique per send;
contentId: "chart", // not on forwards. Not counted in hasAttachments.
}],
trackOpens: true, // optional; embed a tracking pixel
});
// trackOpens tracks sends only when an HTML body is present. Opens surface
// on the returned Message as sent.firstOpenedAt / sent.openCount (an upper
// bound — image proxies prefetch pixels; pixels can also raise spam scores).
//
// sendEmail / replyAllEmail / forwardEmail all throw StorageLimitExceededError
// (402) when the mailbox is at its storage cap — see "Storage cap (402)" below.
Drafts
typescript
const draft = await identity.createEmailDraft({
subject: "Work in progress",
idempotencyKey: "draft-create-2026-08-19-1",
});
for await (const saved of identity.iterEmailDrafts()) {
console.log(saved.id, saved.generation);
}
let current = await identity.getEmailDraft(draft.id);
current = await identity.updateEmailDraft(current.id, {
generation: current.generation,
recipients: { to: ["user@example.com"] },
subject: null, // explicit null clears; omission leaves unchanged
});
current = await inkbox.drafts.addAttachments(identity.emailAddress!, current.id,
current.generation, [{
filename: "notes.txt",
contentType: "text/plain",
contentBase64: "bm90ZXM=",
}]);
const part = current.attachmentMetadata[0];
const content = await inkbox.drafts.downloadAttachment(
identity.emailAddress!, current.id, part.partIndex, current.generation,
);
current = await inkbox.drafts.removeAttachment(
identity.emailAddress!, current.id, part.partIndex, current.generation,
);
const copy = await identity.duplicateEmailDraft(current.id, current.generation);
await identity.deleteEmailDraft(copy.id, copy.generation);
const sent = await identity.sendEmailDraft(current.id, current.generation);

Drafts share the mailbox's standard Drafts folder with connected mail clients. Reuse one idempotencyKey and the exact same request when retrying a logical create after an ambiguous result. Use a new key after the original draft is sent or deleted. Forward-only options require forwardMessageId. Use the latest returned generation for every mutation. A partIndex belongs to the generation that returned it, so refresh attachment metadata after edits.

Successful send returns a Message and removes the draft; an exact-generation retry may return the same sent message. HTTP 409 errors remain structured on InkboxAPIError.detail.error: refresh on draft_generation_conflict and retry the same ID and generation on draft_send_in_progress. Never resend draft_delivery_uncertain; after checking sent mail, duplicate or delete it instead.

Read
typescript
// Iterate all messages — auto-paginated async generator
for await (const msg of identity.iterEmails()) {
console.log(msg.subject, msg.fromAddress, msg.isRead);
}
// Filter by direction
for await (const msg of identity.iterEmails({ direction: "inbound" })) { // or "outbound"
...
}
// Unread only (client-side filtered)
for await (const msg of identity.iterUnreadEmails()) {
...
}
// Mark as read
const ids: string[] = [];
for await (const msg of identity.iterUnreadEmails()) ids.push(msg.id);
await identity.markEmailsRead(ids);
await identity.markEmailsUnread(ids); // batch counterpart
// Note: fetching a single inbound message by id (inkbox.messages.get) with
// an API key marks it read server-side; iterating does not, so
// markEmailsRead is the way to clear unread for list-only workflows. isRead
// (agent consumed via API) is distinct from firstOpenedAt (recipient's mail
// client loaded the tracking pixel).
// Get full thread (oldest-first)
const thread = await identity.getThread(msg.threadId);
for (const m of thread.messages) {
console.log(`[${m.fromAddress}] ${m.subject}`);
}
Thread Folders

Threads carry a folder field: inbox, spam, archive, or blocked (server-assigned, never client-set).

typescript
import { ThreadFolder } from "@inkbox/sdk";
// thread.folder / threadDetail.folder is always one of the four values above.

Low-level folder listing / per-thread updates (list({ folder }), listFolders(email), update(..., { folder })) live on ThreadsResource. Passing folder: "blocked" to update throws before the HTTP call.

Storage cap (402)

Every mailbox has a plan storage cap. All three send paths — sendEmail, replyAllEmail, and forwardEmail (and the inkbox.messages.* equivalents) — throw StorageLimitExceededError (HTTP 402) when the send would push the mailbox over it.

typescript
import { StorageLimitExceededError } from "@inkbox/sdk";

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
100
Repository forks
26
7-day trend score
0

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/inkbox-ai/inkbox/inkbox-ts/badge)](https://verified-skill.com/skills/inkbox-ai/inkbox/inkbox-ts)

Something wrong with this skill or its page? Tell us.