ironpost 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # Ironpost SDK
2
+
3
+ [![npm version](https://img.shields.io/npm/v/ironpost.svg)](https://www.npmjs.com/package/ironpost)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ The official TypeScript SDK for [Ironpost](https://ironpost.ai) — email inboxes for AI agents.
7
+
8
+ Zero dependencies. Works in Node.js, Deno, Bun, Cloudflare Workers, and anywhere with native `fetch`.
9
+
10
+ ## Why Ironpost?
11
+
12
+ AI agents need to send and receive email, but existing APIs (SendGrid, Resend, Mailgun) are built for human-configured transactional workflows. Ironpost gives each agent its own email address and persistent inbox with thread history, webhook delivery, and built-in spam/phishing protection — purpose-built for autonomous agents.
13
+
14
+ | | Ironpost | SendGrid / Resend | Raw SMTP |
15
+ |---|---|---|---|
16
+ | Agent gets own email address | ✅ Auto-generated | ❌ Shared sender | ❌ Manual config |
17
+ | Persistent inbox with history | ✅ Query threads, unreads | ❌ Send-only | ❌ Build it yourself |
18
+ | Agent-to-agent (free) | ✅ Instant, $0 | ❌ Per-message | ❌ Per-message |
19
+ | Spam & phishing protection | ✅ Built-in | ❌ None | ❌ Build it yourself |
20
+ | Real-time webhooks | ✅ HMAC-signed | ⚠️ Varies | ❌ Build it yourself |
21
+ | Zero dependencies | ✅ Native fetch | ❌ Heavy SDKs | ❌ nodemailer, etc. |
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install ironpost
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```ts
32
+ import { Ironpost } from "ironpost";
33
+
34
+ // 1. Create your org (one-time)
35
+ const { organization, apiKey } = await Ironpost.setup({
36
+ orgName: "Acme Corp",
37
+ ownerEmail: "founders@acme.com",
38
+ });
39
+
40
+ // 2. Initialize the client
41
+ const ironpost = new Ironpost(apiKey.key);
42
+
43
+ // 3. Create an agent (gets auto-generated address like brave-eagle-fly@ironpost.email)
44
+ const agent = await ironpost.agents.create({
45
+ name: "Customer Service Agent",
46
+ });
47
+
48
+ // 4. Send a message
49
+ await ironpost.messages.send({
50
+ from: agent.id,
51
+ to: "user@example.com",
52
+ subject: "How can I help you?",
53
+ body: "Send me your request and I will process it.",
54
+ });
55
+
56
+ // 5. Listen for replies
57
+ await ironpost.webhooks.register(agent.id, {
58
+ url: "https://my-app.com/api/webhooks/ironpost",
59
+ events: ["message.received"],
60
+ });
61
+ ```
62
+
63
+ ## Use Cases
64
+
65
+ **Customer support agent** — Give your support bot an email address. Customers email it directly, the agent processes and replies.
66
+
67
+ **Multi-agent workflows** — Agents email each other for free. A research agent emails findings to an analysis agent, which emails a summary to a reporting agent.
68
+
69
+ **Notification handler** — Forward SaaS alerts (Stripe, GitHub, etc.) to an agent that monitors, triages, and escalates.
70
+
71
+ **Outbound campaigns** — Agent sends personalized emails to leads at $0.001/email with full deliverability (SPF, DKIM, DMARC).
72
+
73
+ ## API Overview
74
+
75
+ ### Agents
76
+ ```ts
77
+ const agent = await ironpost.agents.create({ name: "Support" });
78
+ const agents = await ironpost.agents.list();
79
+ const one = await ironpost.agents.get(agentId);
80
+ await ironpost.agents.update(agentId, { name: "New Name" });
81
+ await ironpost.agents.delete(agentId);
82
+ ```
83
+
84
+ ### Messages
85
+ ```ts
86
+ await ironpost.messages.send({ from: agentId, to: "user@gmail.com", subject: "Hi", body: "Hello" });
87
+ const msg = await ironpost.messages.get(messageId);
88
+ await ironpost.messages.reply(messageId, { from: agentId, body: "Thanks!" });
89
+ ```
90
+
91
+ ### Inbox & Threads
92
+ ```ts
93
+ const inbox = await ironpost.agents.messages(agentId, { limit: 20 });
94
+ const threads = await ironpost.agents.threads(agentId);
95
+ const thread = await ironpost.agents.thread(agentId, threadId);
96
+ ```
97
+
98
+ ### Webhooks
99
+ ```ts
100
+ await ironpost.webhooks.register(agentId, { url: "https://...", events: ["message.received"] });
101
+ const hooks = await ironpost.webhooks.list(agentId);
102
+ await ironpost.webhooks.delete(agentId, webhookId);
103
+
104
+ // Verify incoming webhook signatures
105
+ const isValid = await Ironpost.verifyWebhook(rawBody, signatureHeader, webhookSecret);
106
+ ```
107
+
108
+ ### Attachments
109
+ ```ts
110
+ const attachment = await ironpost.attachments.upload({
111
+ agentId,
112
+ filename: "report.pdf",
113
+ content: base64String,
114
+ });
115
+ const response = await ironpost.attachments.download(attachmentId);
116
+ ```
117
+
118
+ ## Configuration
119
+
120
+ ```ts
121
+ // API key as string
122
+ const client = new Ironpost("ip_key_live_...");
123
+
124
+ // Or via environment variable (auto-detected)
125
+ // Set IRONPOST_API_KEY in your environment
126
+ const client = new Ironpost();
127
+
128
+ // Or with options object
129
+ const client = new Ironpost({
130
+ apiKey: "ip_key_live_...",
131
+ baseUrl: "https://api.ironpost.ai", // default
132
+ });
133
+ ```
134
+
135
+ ## Pricing
136
+
137
+ - **In-network** (agent ↔ agent): Free, instant
138
+ - **Inbound** (anyone → agent): Free
139
+ - **Outbound** (agent → external): $0.001/email
140
+ - **Free tier**: 5 agents, 100 outbound/month
141
+
142
+ ## Documentation
143
+
144
+ Full API reference: [ironpost.ai/docs.html](https://ironpost.ai/docs.html)
145
+
146
+ ## License
147
+
148
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Ironpost: () => Ironpost,
24
+ IronpostError: () => IronpostError
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+ var IronpostError = class extends Error {
28
+ constructor(message, status, body) {
29
+ super(message);
30
+ this.status = status;
31
+ this.body = body;
32
+ this.name = "IronpostError";
33
+ }
34
+ };
35
+ var HttpClient = class {
36
+ constructor(baseUrl, apiKey) {
37
+ this.baseUrl = baseUrl;
38
+ this.apiKey = apiKey;
39
+ }
40
+ async request(method, path, body) {
41
+ const url = `${this.baseUrl}${path}`;
42
+ const headers = {
43
+ Authorization: `Bearer ${this.apiKey}`,
44
+ "Content-Type": "application/json"
45
+ };
46
+ const res = await fetch(url, {
47
+ method,
48
+ headers,
49
+ body: body ? JSON.stringify(body) : void 0
50
+ });
51
+ const data = await res.json();
52
+ if (!res.ok) {
53
+ throw new IronpostError(
54
+ data.error ?? `HTTP ${res.status}`,
55
+ res.status,
56
+ data
57
+ );
58
+ }
59
+ return data;
60
+ }
61
+ get(path) {
62
+ return this.request("GET", path);
63
+ }
64
+ post(path, body) {
65
+ return this.request("POST", path, body);
66
+ }
67
+ patch(path, body) {
68
+ return this.request("PATCH", path, body);
69
+ }
70
+ delete(path) {
71
+ return this.request("DELETE", path);
72
+ }
73
+ };
74
+ var AgentsClient = class {
75
+ constructor(http) {
76
+ this.http = http;
77
+ }
78
+ /** Create a new agent. */
79
+ create(params) {
80
+ return this.http.post("/v1/agents", params);
81
+ }
82
+ /** List all agents in your organization. */
83
+ list() {
84
+ return this.http.get("/v1/agents");
85
+ }
86
+ /** Get a single agent by ID. */
87
+ get(agentId) {
88
+ return this.http.get(`/v1/agents/${agentId}`);
89
+ }
90
+ /** Update an agent. */
91
+ update(agentId, params) {
92
+ return this.http.patch(`/v1/agents/${agentId}`, params);
93
+ }
94
+ /** Delete an agent and all associated data. */
95
+ delete(agentId) {
96
+ return this.http.delete(`/v1/agents/${agentId}`);
97
+ }
98
+ /** List inbox messages for an agent. */
99
+ messages(agentId, opts) {
100
+ const params = new URLSearchParams();
101
+ if (opts?.limit) params.set("limit", String(opts.limit));
102
+ if (opts?.cursor) params.set("cursor", opts.cursor);
103
+ const qs = params.toString();
104
+ return this.http.get(`/v1/agents/${agentId}/messages${qs ? `?${qs}` : ""}`);
105
+ }
106
+ /** List threads for an agent. */
107
+ threads(agentId) {
108
+ return this.http.get(`/v1/agents/${agentId}/threads`);
109
+ }
110
+ /** Get a thread and its messages. */
111
+ thread(agentId, threadId) {
112
+ return this.http.get(`/v1/agents/${agentId}/threads/${threadId}`);
113
+ }
114
+ };
115
+ var MessagesClient = class {
116
+ constructor(http) {
117
+ this.http = http;
118
+ }
119
+ /** Send a message. If both sides are on Ironpost, delivery is instant and free. */
120
+ send(params) {
121
+ return this.http.post("/v1/messages", params);
122
+ }
123
+ /** Get a single message by ID. */
124
+ get(messageId) {
125
+ return this.http.get(`/v1/messages/${messageId}`);
126
+ }
127
+ /** Reply in the same thread as an existing message. */
128
+ reply(messageId, params) {
129
+ return this.http.post(`/v1/messages/${messageId}/reply`, params);
130
+ }
131
+ };
132
+ var WebhooksClient = class {
133
+ constructor(http) {
134
+ this.http = http;
135
+ }
136
+ /** Register a webhook for an agent. */
137
+ register(agentId, params) {
138
+ return this.http.post(
139
+ `/v1/agents/${agentId}/webhooks`,
140
+ params
141
+ );
142
+ }
143
+ /** List webhooks for an agent. */
144
+ list(agentId) {
145
+ return this.http.get(`/v1/agents/${agentId}/webhooks`);
146
+ }
147
+ /** Delete a webhook. */
148
+ delete(agentId, webhookId) {
149
+ return this.http.delete(
150
+ `/v1/agents/${agentId}/webhooks/${webhookId}`
151
+ );
152
+ }
153
+ };
154
+ var AttachmentsClient = class {
155
+ constructor(http) {
156
+ this.http = http;
157
+ }
158
+ /** Upload an attachment. Returns metadata with `id` for referencing. */
159
+ upload(params) {
160
+ return this.http.post("/v1/attachments", params);
161
+ }
162
+ /** Download an attachment by ID. Returns the raw Response for streaming. */
163
+ async download(attachmentId) {
164
+ const url = `${this.http.baseUrl}/v1/attachments/${attachmentId}`;
165
+ return fetch(url, {
166
+ headers: {
167
+ Authorization: `Bearer ${this.http.apiKey}`
168
+ }
169
+ });
170
+ }
171
+ };
172
+ var Ironpost = class {
173
+ agents;
174
+ messages;
175
+ webhooks;
176
+ attachments;
177
+ constructor(apiKeyOrOpts, extraOpts) {
178
+ let apiKey;
179
+ let baseUrl;
180
+ if (typeof apiKeyOrOpts === "string") {
181
+ apiKey = apiKeyOrOpts;
182
+ baseUrl = extraOpts?.baseUrl;
183
+ } else if (apiKeyOrOpts) {
184
+ apiKey = apiKeyOrOpts.apiKey;
185
+ baseUrl = apiKeyOrOpts.baseUrl;
186
+ }
187
+ if (!apiKey && typeof process !== "undefined" && process.env?.IRONPOST_API_KEY) {
188
+ apiKey = process.env.IRONPOST_API_KEY;
189
+ }
190
+ if (!apiKey) {
191
+ throw new Error(
192
+ "Ironpost SDK requires an API key. Pass it to the constructor or set IRONPOST_API_KEY."
193
+ );
194
+ }
195
+ baseUrl = baseUrl ?? "https://api.ironpost.ai";
196
+ const http = new HttpClient(baseUrl, apiKey);
197
+ this.agents = new AgentsClient(http);
198
+ this.messages = new MessagesClient(http);
199
+ this.webhooks = new WebhooksClient(http);
200
+ this.attachments = new AttachmentsClient(http);
201
+ }
202
+ // ── Static Helpers ──────────────────────────────────────────────
203
+ /**
204
+ * Provision a new Organization and generate the initial API key.
205
+ * This endpoint is meant for platform setup or bootstrapping environments.
206
+ *
207
+ * @param params orgName and ownerEmail for the new organization
208
+ * @param opts Optional configuration (like replacing baseUrl for local dev)
209
+ * @returns The created Organization and the one-time raw API key.
210
+ */
211
+ static async setup(params, opts) {
212
+ const url = `${opts?.baseUrl ?? "https://api.ironpost.ai"}/v1/setup`;
213
+ const res = await fetch(url, {
214
+ method: "POST",
215
+ headers: { "Content-Type": "application/json" },
216
+ body: JSON.stringify(params)
217
+ });
218
+ const data = await res.json();
219
+ if (!res.ok) {
220
+ throw new IronpostError(
221
+ data.error ?? `HTTP ${res.status}`,
222
+ res.status,
223
+ data
224
+ );
225
+ }
226
+ return data;
227
+ }
228
+ /**
229
+ * Verify an incoming webhook signature.
230
+ *
231
+ * @param payload Raw request body (string)
232
+ * @param signature Value of the X-Ironpost-Signature header
233
+ * @param secret The webhook secret returned at registration
234
+ * @returns true if the signature is valid
235
+ */
236
+ static async verifyWebhook(payload, signature, secret) {
237
+ const enc = new TextEncoder();
238
+ const key = await crypto.subtle.importKey(
239
+ "raw",
240
+ enc.encode(secret),
241
+ { name: "HMAC", hash: "SHA-256" },
242
+ false,
243
+ ["sign"]
244
+ );
245
+ const sig = await crypto.subtle.sign("HMAC", key, enc.encode(payload));
246
+ const expected = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
247
+ return expected === signature;
248
+ }
249
+ };
250
+ // Annotate the CommonJS export names for ESM import in node:
251
+ 0 && (module.exports = {
252
+ Ironpost,
253
+ IronpostError
254
+ });
@@ -0,0 +1,243 @@
1
+ /**
2
+ * packages/sdk — TypeScript SDK for Ironpost.
3
+ *
4
+ * Published to npm as "ironpost".
5
+ * Zero dependencies — uses native fetch.
6
+ *
7
+ * All types are defined inline to avoid coupling with internal packages.
8
+ */
9
+ interface Organization {
10
+ id: string;
11
+ name: string;
12
+ plan: "free" | "paid";
13
+ ownerEmail: string;
14
+ emailVerified: boolean;
15
+ egressQuota: number;
16
+ agentQuota: number;
17
+ createdAt: string;
18
+ }
19
+ interface Agent {
20
+ id: string;
21
+ orgId: string;
22
+ name: string;
23
+ slug: string;
24
+ address: string;
25
+ endpointUrl?: string;
26
+ reputationScore: number;
27
+ createdAt: string;
28
+ updatedAt: string;
29
+ }
30
+ interface Message {
31
+ id: string;
32
+ threadId?: string;
33
+ sender: string;
34
+ recipient: string;
35
+ type: "internal" | "egress" | "ingress";
36
+ subject?: string;
37
+ body: string;
38
+ attachments?: Attachment[];
39
+ score?: number;
40
+ scoreAction?: "accept" | "flag" | "quarantine" | "reject";
41
+ status: "delivered" | "failed" | "quarantined" | "pending";
42
+ inNetwork: boolean;
43
+ messageId?: string;
44
+ inReplyTo?: string;
45
+ sentAt?: string;
46
+ receivedAt?: string;
47
+ deliveryMs?: number;
48
+ createdAt: string;
49
+ }
50
+ interface Thread {
51
+ id: string;
52
+ subject?: string;
53
+ participants: string[];
54
+ messageCount: number;
55
+ createdAt: string;
56
+ updatedAt: string;
57
+ }
58
+ interface Webhook {
59
+ id: string;
60
+ agentId: string;
61
+ url: string;
62
+ secret: string;
63
+ events: string[];
64
+ enabled: boolean;
65
+ createdAt: string;
66
+ }
67
+ interface Attachment {
68
+ id: string;
69
+ filename: string;
70
+ contentType: string;
71
+ size: number;
72
+ key: string;
73
+ }
74
+ interface IronpostOptions {
75
+ /** Your API key (ip_key_...) */
76
+ apiKey: string;
77
+ /** Override the base URL (default: https://api.ironpost.ai) */
78
+ baseUrl?: string;
79
+ }
80
+ interface CreateAgentParams {
81
+ name: string;
82
+ endpointUrl?: string;
83
+ }
84
+ interface UpdateAgentParams {
85
+ name?: string;
86
+ endpointUrl?: string | null;
87
+ }
88
+ interface SendMessageParams {
89
+ from: string;
90
+ to: string;
91
+ subject?: string;
92
+ body: string;
93
+ threadId?: string;
94
+ attachments?: {
95
+ filename: string;
96
+ contentType?: string;
97
+ content: string;
98
+ }[];
99
+ }
100
+ interface UploadAttachmentParams {
101
+ agentId: string;
102
+ filename: string;
103
+ contentType?: string;
104
+ content: string;
105
+ }
106
+ interface ReplyParams {
107
+ from: string;
108
+ body: string;
109
+ subject?: string;
110
+ }
111
+ interface RegisterWebhookParams {
112
+ url: string;
113
+ events?: string[];
114
+ }
115
+ interface ListOptions {
116
+ limit?: number;
117
+ cursor?: string;
118
+ }
119
+ declare class IronpostError extends Error {
120
+ status: number;
121
+ body: unknown;
122
+ constructor(message: string, status: number, body: unknown);
123
+ }
124
+ declare class HttpClient {
125
+ private baseUrl;
126
+ private apiKey;
127
+ constructor(baseUrl: string, apiKey: string);
128
+ private request;
129
+ get<T>(path: string): Promise<T>;
130
+ post<T>(path: string, body?: unknown): Promise<T>;
131
+ patch<T>(path: string, body?: unknown): Promise<T>;
132
+ delete<T>(path: string): Promise<T>;
133
+ }
134
+ declare class AgentsClient {
135
+ private http;
136
+ constructor(http: HttpClient);
137
+ /** Create a new agent. */
138
+ create(params: CreateAgentParams): Promise<Agent>;
139
+ /** List all agents in your organization. */
140
+ list(): Promise<{
141
+ agents: Agent[];
142
+ total: number;
143
+ }>;
144
+ /** Get a single agent by ID. */
145
+ get(agentId: string): Promise<Agent>;
146
+ /** Update an agent. */
147
+ update(agentId: string, params: UpdateAgentParams): Promise<Agent>;
148
+ /** Delete an agent and all associated data. */
149
+ delete(agentId: string): Promise<{
150
+ deleted: boolean;
151
+ }>;
152
+ /** List inbox messages for an agent. */
153
+ messages(agentId: string, opts?: ListOptions): Promise<{
154
+ messages: Message[];
155
+ cursor?: string;
156
+ }>;
157
+ /** List threads for an agent. */
158
+ threads(agentId: string): Promise<{
159
+ threads: Thread[];
160
+ total: number;
161
+ }>;
162
+ /** Get a thread and its messages. */
163
+ thread(agentId: string, threadId: string): Promise<{
164
+ thread: Thread;
165
+ messages: Message[];
166
+ }>;
167
+ }
168
+ declare class MessagesClient {
169
+ private http;
170
+ constructor(http: HttpClient);
171
+ /** Send a message. If both sides are on Ironpost, delivery is instant and free. */
172
+ send(params: SendMessageParams): Promise<Message>;
173
+ /** Get a single message by ID. */
174
+ get(messageId: string): Promise<Message>;
175
+ /** Reply in the same thread as an existing message. */
176
+ reply(messageId: string, params: ReplyParams): Promise<Message>;
177
+ }
178
+ declare class WebhooksClient {
179
+ private http;
180
+ constructor(http: HttpClient);
181
+ /** Register a webhook for an agent. */
182
+ register(agentId: string, params: RegisterWebhookParams): Promise<Webhook>;
183
+ /** List webhooks for an agent. */
184
+ list(agentId: string): Promise<{
185
+ webhooks: Webhook[];
186
+ total: number;
187
+ }>;
188
+ /** Delete a webhook. */
189
+ delete(agentId: string, webhookId: string): Promise<{
190
+ deleted: boolean;
191
+ }>;
192
+ }
193
+ declare class AttachmentsClient {
194
+ private http;
195
+ constructor(http: HttpClient);
196
+ /** Upload an attachment. Returns metadata with `id` for referencing. */
197
+ upload(params: UploadAttachmentParams): Promise<Attachment>;
198
+ /** Download an attachment by ID. Returns the raw Response for streaming. */
199
+ download(attachmentId: string): Promise<Response>;
200
+ }
201
+ declare class Ironpost {
202
+ readonly agents: AgentsClient;
203
+ readonly messages: MessagesClient;
204
+ readonly webhooks: WebhooksClient;
205
+ readonly attachments: AttachmentsClient;
206
+ constructor(apiKey?: string, opts?: {
207
+ baseUrl?: string;
208
+ });
209
+ constructor(opts?: IronpostOptions);
210
+ /**
211
+ * Provision a new Organization and generate the initial API key.
212
+ * This endpoint is meant for platform setup or bootstrapping environments.
213
+ *
214
+ * @param params orgName and ownerEmail for the new organization
215
+ * @param opts Optional configuration (like replacing baseUrl for local dev)
216
+ * @returns The created Organization and the one-time raw API key.
217
+ */
218
+ static setup(params: {
219
+ orgName: string;
220
+ ownerEmail: string;
221
+ }, opts?: {
222
+ baseUrl?: string;
223
+ }): Promise<{
224
+ organization: Organization;
225
+ apiKey: {
226
+ id: string;
227
+ key: string;
228
+ prefix: string;
229
+ scopes: string[];
230
+ };
231
+ }>;
232
+ /**
233
+ * Verify an incoming webhook signature.
234
+ *
235
+ * @param payload Raw request body (string)
236
+ * @param signature Value of the X-Ironpost-Signature header
237
+ * @param secret The webhook secret returned at registration
238
+ * @returns true if the signature is valid
239
+ */
240
+ static verifyWebhook(payload: string, signature: string, secret: string): Promise<boolean>;
241
+ }
242
+
243
+ export { type Agent, type Attachment, type CreateAgentParams, Ironpost, IronpostError, type IronpostOptions, type ListOptions, type Message, type Organization, type RegisterWebhookParams, type ReplyParams, type SendMessageParams, type Thread, type UpdateAgentParams, type UploadAttachmentParams, type Webhook };
@@ -0,0 +1,243 @@
1
+ /**
2
+ * packages/sdk — TypeScript SDK for Ironpost.
3
+ *
4
+ * Published to npm as "ironpost".
5
+ * Zero dependencies — uses native fetch.
6
+ *
7
+ * All types are defined inline to avoid coupling with internal packages.
8
+ */
9
+ interface Organization {
10
+ id: string;
11
+ name: string;
12
+ plan: "free" | "paid";
13
+ ownerEmail: string;
14
+ emailVerified: boolean;
15
+ egressQuota: number;
16
+ agentQuota: number;
17
+ createdAt: string;
18
+ }
19
+ interface Agent {
20
+ id: string;
21
+ orgId: string;
22
+ name: string;
23
+ slug: string;
24
+ address: string;
25
+ endpointUrl?: string;
26
+ reputationScore: number;
27
+ createdAt: string;
28
+ updatedAt: string;
29
+ }
30
+ interface Message {
31
+ id: string;
32
+ threadId?: string;
33
+ sender: string;
34
+ recipient: string;
35
+ type: "internal" | "egress" | "ingress";
36
+ subject?: string;
37
+ body: string;
38
+ attachments?: Attachment[];
39
+ score?: number;
40
+ scoreAction?: "accept" | "flag" | "quarantine" | "reject";
41
+ status: "delivered" | "failed" | "quarantined" | "pending";
42
+ inNetwork: boolean;
43
+ messageId?: string;
44
+ inReplyTo?: string;
45
+ sentAt?: string;
46
+ receivedAt?: string;
47
+ deliveryMs?: number;
48
+ createdAt: string;
49
+ }
50
+ interface Thread {
51
+ id: string;
52
+ subject?: string;
53
+ participants: string[];
54
+ messageCount: number;
55
+ createdAt: string;
56
+ updatedAt: string;
57
+ }
58
+ interface Webhook {
59
+ id: string;
60
+ agentId: string;
61
+ url: string;
62
+ secret: string;
63
+ events: string[];
64
+ enabled: boolean;
65
+ createdAt: string;
66
+ }
67
+ interface Attachment {
68
+ id: string;
69
+ filename: string;
70
+ contentType: string;
71
+ size: number;
72
+ key: string;
73
+ }
74
+ interface IronpostOptions {
75
+ /** Your API key (ip_key_...) */
76
+ apiKey: string;
77
+ /** Override the base URL (default: https://api.ironpost.ai) */
78
+ baseUrl?: string;
79
+ }
80
+ interface CreateAgentParams {
81
+ name: string;
82
+ endpointUrl?: string;
83
+ }
84
+ interface UpdateAgentParams {
85
+ name?: string;
86
+ endpointUrl?: string | null;
87
+ }
88
+ interface SendMessageParams {
89
+ from: string;
90
+ to: string;
91
+ subject?: string;
92
+ body: string;
93
+ threadId?: string;
94
+ attachments?: {
95
+ filename: string;
96
+ contentType?: string;
97
+ content: string;
98
+ }[];
99
+ }
100
+ interface UploadAttachmentParams {
101
+ agentId: string;
102
+ filename: string;
103
+ contentType?: string;
104
+ content: string;
105
+ }
106
+ interface ReplyParams {
107
+ from: string;
108
+ body: string;
109
+ subject?: string;
110
+ }
111
+ interface RegisterWebhookParams {
112
+ url: string;
113
+ events?: string[];
114
+ }
115
+ interface ListOptions {
116
+ limit?: number;
117
+ cursor?: string;
118
+ }
119
+ declare class IronpostError extends Error {
120
+ status: number;
121
+ body: unknown;
122
+ constructor(message: string, status: number, body: unknown);
123
+ }
124
+ declare class HttpClient {
125
+ private baseUrl;
126
+ private apiKey;
127
+ constructor(baseUrl: string, apiKey: string);
128
+ private request;
129
+ get<T>(path: string): Promise<T>;
130
+ post<T>(path: string, body?: unknown): Promise<T>;
131
+ patch<T>(path: string, body?: unknown): Promise<T>;
132
+ delete<T>(path: string): Promise<T>;
133
+ }
134
+ declare class AgentsClient {
135
+ private http;
136
+ constructor(http: HttpClient);
137
+ /** Create a new agent. */
138
+ create(params: CreateAgentParams): Promise<Agent>;
139
+ /** List all agents in your organization. */
140
+ list(): Promise<{
141
+ agents: Agent[];
142
+ total: number;
143
+ }>;
144
+ /** Get a single agent by ID. */
145
+ get(agentId: string): Promise<Agent>;
146
+ /** Update an agent. */
147
+ update(agentId: string, params: UpdateAgentParams): Promise<Agent>;
148
+ /** Delete an agent and all associated data. */
149
+ delete(agentId: string): Promise<{
150
+ deleted: boolean;
151
+ }>;
152
+ /** List inbox messages for an agent. */
153
+ messages(agentId: string, opts?: ListOptions): Promise<{
154
+ messages: Message[];
155
+ cursor?: string;
156
+ }>;
157
+ /** List threads for an agent. */
158
+ threads(agentId: string): Promise<{
159
+ threads: Thread[];
160
+ total: number;
161
+ }>;
162
+ /** Get a thread and its messages. */
163
+ thread(agentId: string, threadId: string): Promise<{
164
+ thread: Thread;
165
+ messages: Message[];
166
+ }>;
167
+ }
168
+ declare class MessagesClient {
169
+ private http;
170
+ constructor(http: HttpClient);
171
+ /** Send a message. If both sides are on Ironpost, delivery is instant and free. */
172
+ send(params: SendMessageParams): Promise<Message>;
173
+ /** Get a single message by ID. */
174
+ get(messageId: string): Promise<Message>;
175
+ /** Reply in the same thread as an existing message. */
176
+ reply(messageId: string, params: ReplyParams): Promise<Message>;
177
+ }
178
+ declare class WebhooksClient {
179
+ private http;
180
+ constructor(http: HttpClient);
181
+ /** Register a webhook for an agent. */
182
+ register(agentId: string, params: RegisterWebhookParams): Promise<Webhook>;
183
+ /** List webhooks for an agent. */
184
+ list(agentId: string): Promise<{
185
+ webhooks: Webhook[];
186
+ total: number;
187
+ }>;
188
+ /** Delete a webhook. */
189
+ delete(agentId: string, webhookId: string): Promise<{
190
+ deleted: boolean;
191
+ }>;
192
+ }
193
+ declare class AttachmentsClient {
194
+ private http;
195
+ constructor(http: HttpClient);
196
+ /** Upload an attachment. Returns metadata with `id` for referencing. */
197
+ upload(params: UploadAttachmentParams): Promise<Attachment>;
198
+ /** Download an attachment by ID. Returns the raw Response for streaming. */
199
+ download(attachmentId: string): Promise<Response>;
200
+ }
201
+ declare class Ironpost {
202
+ readonly agents: AgentsClient;
203
+ readonly messages: MessagesClient;
204
+ readonly webhooks: WebhooksClient;
205
+ readonly attachments: AttachmentsClient;
206
+ constructor(apiKey?: string, opts?: {
207
+ baseUrl?: string;
208
+ });
209
+ constructor(opts?: IronpostOptions);
210
+ /**
211
+ * Provision a new Organization and generate the initial API key.
212
+ * This endpoint is meant for platform setup or bootstrapping environments.
213
+ *
214
+ * @param params orgName and ownerEmail for the new organization
215
+ * @param opts Optional configuration (like replacing baseUrl for local dev)
216
+ * @returns The created Organization and the one-time raw API key.
217
+ */
218
+ static setup(params: {
219
+ orgName: string;
220
+ ownerEmail: string;
221
+ }, opts?: {
222
+ baseUrl?: string;
223
+ }): Promise<{
224
+ organization: Organization;
225
+ apiKey: {
226
+ id: string;
227
+ key: string;
228
+ prefix: string;
229
+ scopes: string[];
230
+ };
231
+ }>;
232
+ /**
233
+ * Verify an incoming webhook signature.
234
+ *
235
+ * @param payload Raw request body (string)
236
+ * @param signature Value of the X-Ironpost-Signature header
237
+ * @param secret The webhook secret returned at registration
238
+ * @returns true if the signature is valid
239
+ */
240
+ static verifyWebhook(payload: string, signature: string, secret: string): Promise<boolean>;
241
+ }
242
+
243
+ export { type Agent, type Attachment, type CreateAgentParams, Ironpost, IronpostError, type IronpostOptions, type ListOptions, type Message, type Organization, type RegisterWebhookParams, type ReplyParams, type SendMessageParams, type Thread, type UpdateAgentParams, type UploadAttachmentParams, type Webhook };
package/dist/index.js ADDED
@@ -0,0 +1,228 @@
1
+ // src/index.ts
2
+ var IronpostError = class extends Error {
3
+ constructor(message, status, body) {
4
+ super(message);
5
+ this.status = status;
6
+ this.body = body;
7
+ this.name = "IronpostError";
8
+ }
9
+ };
10
+ var HttpClient = class {
11
+ constructor(baseUrl, apiKey) {
12
+ this.baseUrl = baseUrl;
13
+ this.apiKey = apiKey;
14
+ }
15
+ async request(method, path, body) {
16
+ const url = `${this.baseUrl}${path}`;
17
+ const headers = {
18
+ Authorization: `Bearer ${this.apiKey}`,
19
+ "Content-Type": "application/json"
20
+ };
21
+ const res = await fetch(url, {
22
+ method,
23
+ headers,
24
+ body: body ? JSON.stringify(body) : void 0
25
+ });
26
+ const data = await res.json();
27
+ if (!res.ok) {
28
+ throw new IronpostError(
29
+ data.error ?? `HTTP ${res.status}`,
30
+ res.status,
31
+ data
32
+ );
33
+ }
34
+ return data;
35
+ }
36
+ get(path) {
37
+ return this.request("GET", path);
38
+ }
39
+ post(path, body) {
40
+ return this.request("POST", path, body);
41
+ }
42
+ patch(path, body) {
43
+ return this.request("PATCH", path, body);
44
+ }
45
+ delete(path) {
46
+ return this.request("DELETE", path);
47
+ }
48
+ };
49
+ var AgentsClient = class {
50
+ constructor(http) {
51
+ this.http = http;
52
+ }
53
+ /** Create a new agent. */
54
+ create(params) {
55
+ return this.http.post("/v1/agents", params);
56
+ }
57
+ /** List all agents in your organization. */
58
+ list() {
59
+ return this.http.get("/v1/agents");
60
+ }
61
+ /** Get a single agent by ID. */
62
+ get(agentId) {
63
+ return this.http.get(`/v1/agents/${agentId}`);
64
+ }
65
+ /** Update an agent. */
66
+ update(agentId, params) {
67
+ return this.http.patch(`/v1/agents/${agentId}`, params);
68
+ }
69
+ /** Delete an agent and all associated data. */
70
+ delete(agentId) {
71
+ return this.http.delete(`/v1/agents/${agentId}`);
72
+ }
73
+ /** List inbox messages for an agent. */
74
+ messages(agentId, opts) {
75
+ const params = new URLSearchParams();
76
+ if (opts?.limit) params.set("limit", String(opts.limit));
77
+ if (opts?.cursor) params.set("cursor", opts.cursor);
78
+ const qs = params.toString();
79
+ return this.http.get(`/v1/agents/${agentId}/messages${qs ? `?${qs}` : ""}`);
80
+ }
81
+ /** List threads for an agent. */
82
+ threads(agentId) {
83
+ return this.http.get(`/v1/agents/${agentId}/threads`);
84
+ }
85
+ /** Get a thread and its messages. */
86
+ thread(agentId, threadId) {
87
+ return this.http.get(`/v1/agents/${agentId}/threads/${threadId}`);
88
+ }
89
+ };
90
+ var MessagesClient = class {
91
+ constructor(http) {
92
+ this.http = http;
93
+ }
94
+ /** Send a message. If both sides are on Ironpost, delivery is instant and free. */
95
+ send(params) {
96
+ return this.http.post("/v1/messages", params);
97
+ }
98
+ /** Get a single message by ID. */
99
+ get(messageId) {
100
+ return this.http.get(`/v1/messages/${messageId}`);
101
+ }
102
+ /** Reply in the same thread as an existing message. */
103
+ reply(messageId, params) {
104
+ return this.http.post(`/v1/messages/${messageId}/reply`, params);
105
+ }
106
+ };
107
+ var WebhooksClient = class {
108
+ constructor(http) {
109
+ this.http = http;
110
+ }
111
+ /** Register a webhook for an agent. */
112
+ register(agentId, params) {
113
+ return this.http.post(
114
+ `/v1/agents/${agentId}/webhooks`,
115
+ params
116
+ );
117
+ }
118
+ /** List webhooks for an agent. */
119
+ list(agentId) {
120
+ return this.http.get(`/v1/agents/${agentId}/webhooks`);
121
+ }
122
+ /** Delete a webhook. */
123
+ delete(agentId, webhookId) {
124
+ return this.http.delete(
125
+ `/v1/agents/${agentId}/webhooks/${webhookId}`
126
+ );
127
+ }
128
+ };
129
+ var AttachmentsClient = class {
130
+ constructor(http) {
131
+ this.http = http;
132
+ }
133
+ /** Upload an attachment. Returns metadata with `id` for referencing. */
134
+ upload(params) {
135
+ return this.http.post("/v1/attachments", params);
136
+ }
137
+ /** Download an attachment by ID. Returns the raw Response for streaming. */
138
+ async download(attachmentId) {
139
+ const url = `${this.http.baseUrl}/v1/attachments/${attachmentId}`;
140
+ return fetch(url, {
141
+ headers: {
142
+ Authorization: `Bearer ${this.http.apiKey}`
143
+ }
144
+ });
145
+ }
146
+ };
147
+ var Ironpost = class {
148
+ agents;
149
+ messages;
150
+ webhooks;
151
+ attachments;
152
+ constructor(apiKeyOrOpts, extraOpts) {
153
+ let apiKey;
154
+ let baseUrl;
155
+ if (typeof apiKeyOrOpts === "string") {
156
+ apiKey = apiKeyOrOpts;
157
+ baseUrl = extraOpts?.baseUrl;
158
+ } else if (apiKeyOrOpts) {
159
+ apiKey = apiKeyOrOpts.apiKey;
160
+ baseUrl = apiKeyOrOpts.baseUrl;
161
+ }
162
+ if (!apiKey && typeof process !== "undefined" && process.env?.IRONPOST_API_KEY) {
163
+ apiKey = process.env.IRONPOST_API_KEY;
164
+ }
165
+ if (!apiKey) {
166
+ throw new Error(
167
+ "Ironpost SDK requires an API key. Pass it to the constructor or set IRONPOST_API_KEY."
168
+ );
169
+ }
170
+ baseUrl = baseUrl ?? "https://api.ironpost.ai";
171
+ const http = new HttpClient(baseUrl, apiKey);
172
+ this.agents = new AgentsClient(http);
173
+ this.messages = new MessagesClient(http);
174
+ this.webhooks = new WebhooksClient(http);
175
+ this.attachments = new AttachmentsClient(http);
176
+ }
177
+ // ── Static Helpers ──────────────────────────────────────────────
178
+ /**
179
+ * Provision a new Organization and generate the initial API key.
180
+ * This endpoint is meant for platform setup or bootstrapping environments.
181
+ *
182
+ * @param params orgName and ownerEmail for the new organization
183
+ * @param opts Optional configuration (like replacing baseUrl for local dev)
184
+ * @returns The created Organization and the one-time raw API key.
185
+ */
186
+ static async setup(params, opts) {
187
+ const url = `${opts?.baseUrl ?? "https://api.ironpost.ai"}/v1/setup`;
188
+ const res = await fetch(url, {
189
+ method: "POST",
190
+ headers: { "Content-Type": "application/json" },
191
+ body: JSON.stringify(params)
192
+ });
193
+ const data = await res.json();
194
+ if (!res.ok) {
195
+ throw new IronpostError(
196
+ data.error ?? `HTTP ${res.status}`,
197
+ res.status,
198
+ data
199
+ );
200
+ }
201
+ return data;
202
+ }
203
+ /**
204
+ * Verify an incoming webhook signature.
205
+ *
206
+ * @param payload Raw request body (string)
207
+ * @param signature Value of the X-Ironpost-Signature header
208
+ * @param secret The webhook secret returned at registration
209
+ * @returns true if the signature is valid
210
+ */
211
+ static async verifyWebhook(payload, signature, secret) {
212
+ const enc = new TextEncoder();
213
+ const key = await crypto.subtle.importKey(
214
+ "raw",
215
+ enc.encode(secret),
216
+ { name: "HMAC", hash: "SHA-256" },
217
+ false,
218
+ ["sign"]
219
+ );
220
+ const sig = await crypto.subtle.sign("HMAC", key, enc.encode(payload));
221
+ const expected = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
222
+ return expected === signature;
223
+ }
224
+ };
225
+ export {
226
+ Ironpost,
227
+ IronpostError
228
+ };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "ironpost",
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "prepublishOnly": "bun run build"
22
+ },
23
+ "license": "MIT",
24
+ "description": "TypeScript SDK for Ironpost - email inboxes for AI agents. Zero dependencies.",
25
+ "homepage": "https://ironpost.ai",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/ironposthq/ironpost-js"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/ironposthq/ironpost-js/issues"
32
+ },
33
+ "author": "Ironpost <hello@ironpost.ai>",
34
+ "keywords": [
35
+ "agent",
36
+ "ai-agent",
37
+ "email",
38
+ "email-api",
39
+ "email-infrastructure",
40
+ "messaging",
41
+ "ai",
42
+ "ironpost",
43
+ "agent-email",
44
+ "agent-messaging",
45
+ "agent-infrastructure",
46
+ "llm",
47
+ "autonomous-agent",
48
+ "webhook",
49
+ "smtp",
50
+ "inbox",
51
+ "ai-infrastructure",
52
+ "mcp",
53
+ "langchain",
54
+ "crewai",
55
+ "agent-communication",
56
+ "agent-to-agent"
57
+ ],
58
+ "devDependencies": {
59
+ "tsup": "^8.3.6",
60
+ "typescript": "^5.7.3"
61
+ }
62
+ }