@jclvsh/iris 0.4.0 → 0.5.1

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 CHANGED
@@ -59,30 +59,30 @@ iris campaigns --help
59
59
 
60
60
  ### CLI Commands
61
61
 
62
- | Group | Alias | Commands | Count |
63
- | --- | --- | --- | --- |
64
- | campaigns | `c` | list, get, create, update, delete, usage, analyze | 7 |
65
- | contacts | `ct` | list, get, create, update, delete, usage | 6 |
66
- | messages | `m` | pending, approve, reject, retry, feedback, bulk | 6 |
67
- | conversations | `conv` | list, get, messages, read | 4 |
68
- | discovery | `d` | search, search-campaign, sources, job | 4 |
69
- | ai | — | generate, generate-campaign, analyze, usage, suggestions | 5 |
70
- | keys | `k` | me, list, create, rename, revoke | 5 |
71
- | webhooks | `w` | list, create, get, update, delete, test, deliveries | 7 |
72
- | notifications | `n` | list, update, delete, read-all | 4 |
73
- | analytics | `a` | overview, campaign, performance, response-time, contact-types | 5 |
74
- | billing | `b` | plans | 1 |
75
- | channels | `ch` | accounts, availability, send | 3 |
76
- | credits | `cr` | balance, buy | 2 |
62
+ | Group | Alias | Commands | Count |
63
+ | ------------- | ------ | ------------------------------------------------------------- | ----- |
64
+ | campaigns | `c` | list, get, create, update, delete, usage, analyze | 7 |
65
+ | contacts | `ct` | list, get, create, update, delete, usage | 6 |
66
+ | messages | `m` | pending, approve, reject, retry, feedback, bulk | 6 |
67
+ | conversations | `conv` | list, get, messages, read | 4 |
68
+ | discovery | `d` | search, search-campaign, sources, job | 4 |
69
+ | ai | — | generate, generate-campaign, analyze, usage | 4 |
70
+ | keys | `k` | me, list, create, rename, revoke | 5 |
71
+ | webhooks | `w` | list, create, get, update, delete, test, deliveries | 7 |
72
+ | notifications | `n` | list, update, delete, read-all | 4 |
73
+ | analytics | `a` | overview, campaign, performance, response-time, contact-types | 5 |
74
+ | billing | `b` | plans | 1 |
75
+ | channels | `ch` | accounts, availability, send | 3 |
76
+ | credits | `cr` | balance, buy | 2 |
77
77
 
78
78
  ## MCP Server Configuration
79
79
 
80
80
  ### Environment Variables
81
81
 
82
- | Variable | Required | Description |
83
- | --- | --- | --- |
84
- | `IRIS_API_KEY` | Yes | API key (must start with `is_live_`) |
85
- | `IRIS_API_URL` | No | API base URL (default: `https://iris-ai.dev/api/v1`) |
82
+ | Variable | Required | Description |
83
+ | -------------- | -------- | ---------------------------------------------------- |
84
+ | `IRIS_API_KEY` | Yes | API key (must start with `is_live_`) |
85
+ | `IRIS_API_URL` | No | API base URL (default: `https://iris-ai.dev/api/v1`) |
86
86
 
87
87
  > **Note:** the args use `-p @jclvsh/iris iris-mcp` to explicitly select the MCP-server bin. The package ships two binaries (`iris` for the CLI, `iris-mcp` for the MCP server), so a bare `npx @jclvsh/iris` would launch the CLI by default.
88
88
 
@@ -128,222 +128,221 @@ Add to `.cursor/mcp.json`:
128
128
 
129
129
  ### Campaigns
130
130
 
131
- | Tool | Description |
132
- | --- | --- |
133
- | `list_campaigns` | List campaigns with optional status filter |
134
- | `create_campaign` | Create a new outreach campaign |
135
- | `get_campaign` | Get a campaign by ID with stats |
136
- | `update_campaign` | Update a campaign's name, status, or follow-up settings |
137
- | `delete_campaign` | Soft-delete a campaign (sets status to `deleted`) |
131
+ | Tool | Description |
132
+ | ------------------------ | ------------------------------------------------------- |
133
+ | `list_campaigns` | List campaigns with optional status filter |
134
+ | `create_campaign` | Create a new outreach campaign |
135
+ | `get_campaign` | Get a campaign by ID with stats |
136
+ | `update_campaign` | Update a campaign's name, status, or follow-up settings |
137
+ | `delete_campaign` | Soft-delete a campaign (sets status to `deleted`) |
138
138
  | `list_campaign_contacts` | List contacts in a campaign with optional status filter |
139
- | `get_campaign_usage` | Get usage limits and current counts for a campaign |
140
- | `analyze_campaign` | Trigger AI analysis to generate target profiles |
141
- | `list_target_profiles` | List target profiles for a campaign |
142
- | `create_target_profile` | Create a target profile for a campaign |
143
- | `delete_target_profile` | Delete a target profile from a campaign |
139
+ | `get_campaign_usage` | Get usage limits and current counts for a campaign |
140
+ | `analyze_campaign` | Trigger AI analysis to generate target profiles |
141
+ | `list_target_profiles` | List target profiles for a campaign |
142
+ | `create_target_profile` | Create a target profile for a campaign |
143
+ | `delete_target_profile` | Delete a target profile from a campaign |
144
144
 
145
145
  #### `create_campaign` Parameters
146
146
 
147
- | Parameter | Type | Required | Description |
148
- | --- | --- | --- | --- |
149
- | `name` | string (1-100) | Yes | Campaign name |
150
- | `objective_type` | enum | Yes | `venue_dj`, `venue_host`, `product_promotion`, `custom` |
151
- | `product_name` | string (≤200) | No | Product name (for `product_promotion`) |
152
- | `product_url` | string (URL) | No | Product URL (for `product_promotion`) |
153
- | `product_description` | string (≤2000) | No | Product description (for `product_promotion`) |
147
+ | Parameter | Type | Required | Description |
148
+ | --------------------- | -------------- | -------- | ------------------------------------------------------- |
149
+ | `name` | string (1-100) | Yes | Campaign name |
150
+ | `objective_type` | enum | Yes | `venue_dj`, `venue_host`, `product_promotion`, `custom` |
151
+ | `product_name` | string (≤200) | No | Product name (for `product_promotion`) |
152
+ | `product_url` | string (URL) | No | Product URL (for `product_promotion`) |
153
+ | `product_description` | string (≤2000) | No | Product description (for `product_promotion`) |
154
154
 
155
155
  #### `update_campaign` Parameters
156
156
 
157
- | Parameter | Type | Required | Description |
158
- | --- | --- | --- | --- |
159
- | `id` | string (UUID) | Yes | Campaign ID |
160
- | `name` | string (1-100) | No | New campaign name |
161
- | `status` | enum | No | `draft`, `active`, `paused`, `completed`, `deleted` |
162
- | `follow_up_enabled` | boolean | No | Enable follow-up emails |
163
- | `follow_up_max_count` | number (0-10) | No | Max follow-up count |
164
- | `follow_up_delay_days` | number (1-30) | No | Days between follow-ups |
157
+ | Parameter | Type | Required | Description |
158
+ | ---------------------- | -------------- | -------- | --------------------------------------------------- |
159
+ | `id` | string (UUID) | Yes | Campaign ID |
160
+ | `name` | string (1-100) | No | New campaign name |
161
+ | `status` | enum | No | `draft`, `active`, `paused`, `completed`, `deleted` |
162
+ | `follow_up_enabled` | boolean | No | Enable follow-up emails |
163
+ | `follow_up_max_count` | number (0-10) | No | Max follow-up count |
164
+ | `follow_up_delay_days` | number (1-30) | No | Days between follow-ups |
165
165
 
166
166
  #### `create_target_profile` Parameters
167
167
 
168
- | Parameter | Type | Required | Description |
169
- | --- | --- | --- | --- |
170
- | `id` | string (UUID) | Yes | Campaign ID |
171
- | `name` | string (1-200) | Yes | Profile name |
172
- | `profile_type` | enum | Yes | `local_business`, `content_creator`, `influencer`, `b2b_professional`, `ecommerce`, `saas_company`, `agency`, `freelancer`, `other` |
173
- | `job_titles` | string[] | No | Target job titles |
174
- | `industries` | string[] | No | Target industries |
175
- | `discovery_keywords` | string[] | No | Keywords for contact discovery |
176
- | `recommended_channels` | string[] | No | `email`, `instagram`, `twitter`, `linkedin` |
168
+ | Parameter | Type | Required | Description |
169
+ | ---------------------- | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
170
+ | `id` | string (UUID) | Yes | Campaign ID |
171
+ | `name` | string (1-200) | Yes | Profile name |
172
+ | `profile_type` | enum | Yes | `local_business`, `content_creator`, `influencer`, `b2b_professional`, `ecommerce`, `saas_company`, `agency`, `freelancer`, `other` |
173
+ | `job_titles` | string[] | No | Target job titles |
174
+ | `industries` | string[] | No | Target industries |
175
+ | `discovery_keywords` | string[] | No | Keywords for contact discovery |
176
+ | `recommended_channels` | string[] | No | `email`, `instagram`, `twitter`, `linkedin` |
177
177
 
178
178
  ### Contacts
179
179
 
180
- | Tool | Description |
181
- | --- | --- |
182
- | `list_contacts` | List contacts with search |
183
- | `create_contact` | Create a new contact |
184
- | `get_contact` | Get a contact by ID |
185
- | `update_contact` | Update a contact's details |
186
- | `delete_contact` | Soft-delete a contact |
187
- | `bulk_import_contacts` | Import up to 500 contacts at once |
188
- | `get_contact_usage` | Get usage limits for the billing period |
180
+ | Tool | Description |
181
+ | ---------------------------- | ----------------------------------------- |
182
+ | `list_contacts` | List contacts with search |
183
+ | `create_contact` | Create a new contact |
184
+ | `get_contact` | Get a contact by ID |
185
+ | `update_contact` | Update a contact's details |
186
+ | `delete_contact` | Soft-delete a contact |
187
+ | `bulk_import_contacts` | Import up to 500 contacts at once |
188
+ | `get_contact_usage` | Get usage limits for the billing period |
189
189
  | `list_contact_conversations` | List conversations for a specific contact |
190
190
 
191
191
  #### `create_contact` Parameters
192
192
 
193
- | Parameter | Type | Required | Description |
194
- | --- | --- | --- | --- |
195
- | `email` | string (email) | Yes | Contact email address |
196
- | `first_name` | string (≤100) | No | First name |
197
- | `last_name` | string (≤100) | No | Last name |
198
- | `company_name` | string (≤200) | No | Company name |
199
- | `job_title` | string (≤200) | No | Job title |
200
- | `phone` | string (≤50) | No | Phone number |
201
- | `website` | string (URL) | No | Website URL |
202
- | `tags` | string[] | No | Tags for categorization |
193
+ | Parameter | Type | Required | Description |
194
+ | -------------- | -------------- | -------- | ----------------------- |
195
+ | `email` | string (email) | Yes | Contact email address |
196
+ | `first_name` | string (≤100) | No | First name |
197
+ | `last_name` | string (≤100) | No | Last name |
198
+ | `company_name` | string (≤200) | No | Company name |
199
+ | `job_title` | string (≤200) | No | Job title |
200
+ | `phone` | string (≤50) | No | Phone number |
201
+ | `website` | string (URL) | No | Website URL |
202
+ | `tags` | string[] | No | Tags for categorization |
203
203
 
204
204
  #### `bulk_import_contacts` Parameters
205
205
 
206
- | Parameter | Type | Required | Description |
207
- | --- | --- | --- | --- |
208
- | `contacts` | object[] (1-500) | Yes | Array of contacts (`email`, `first_name?`, `last_name?`, `company_name?`, `job_title?`, `tags?`) |
209
- | `campaign_id` | string (UUID) | No | Campaign to add contacts to |
206
+ | Parameter | Type | Required | Description |
207
+ | ------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------ |
208
+ | `contacts` | object[] (1-500) | Yes | Array of contacts (`email`, `first_name?`, `last_name?`, `company_name?`, `job_title?`, `tags?`) |
209
+ | `campaign_id` | string (UUID) | No | Campaign to add contacts to |
210
210
 
211
211
  ### Messages
212
212
 
213
- | Tool | Description |
214
- | --- | --- |
215
- | `list_pending_messages` | List messages pending approval |
216
- | `approve_message` | Approve a single pending message |
217
- | `reject_message` | Reject a single pending message |
218
- | `retry_message` | Retry a failed message send |
213
+ | Tool | Description |
214
+ | ------------------------- | ------------------------------------------------ |
215
+ | `list_pending_messages` | List messages pending approval |
216
+ | `approve_message` | Approve a single pending message |
217
+ | `reject_message` | Reject a single pending message |
218
+ | `retry_message` | Retry a failed message send |
219
219
  | `submit_message_feedback` | Submit feedback to improve future AI generations |
220
- | `bulk_message_action` | Bulk approve or reject pending messages |
220
+ | `bulk_message_action` | Bulk approve or reject pending messages |
221
221
 
222
222
  ### Conversations
223
223
 
224
- | Tool | Description |
225
- | --- | --- |
226
- | `list_conversations` | List conversations with optional channel/status filters |
227
- | `get_conversation` | Get a conversation by ID |
228
- | `get_conversation_messages` | List messages in a conversation |
229
- | `mark_conversation_read` | Mark a conversation as read |
230
- | `get_engagement_metrics` | Get engagement metrics across conversations |
224
+ | Tool | Description |
225
+ | --------------------------- | ------------------------------------------------------- |
226
+ | `list_conversations` | List conversations with optional channel/status filters |
227
+ | `get_conversation` | Get a conversation by ID |
228
+ | `get_conversation_messages` | List messages in a conversation |
229
+ | `mark_conversation_read` | Mark a conversation as read |
230
+ | `get_engagement_metrics` | Get engagement metrics across conversations |
231
231
 
232
232
  ### AI
233
233
 
234
- | Tool | Description |
235
- | --- | --- |
236
- | `generate_content` | Generate AI content for a contact conversation (initial email, follow-up, reply) |
237
- | `generate_campaign_content` | Generate campaign-level content (email templates, subject lines, briefs) |
238
- | `analyze_response` | Analyze inbound message sentiment, intent, and suggested action |
239
- | `get_ai_usage` | Get AI generation usage limits |
240
- | `get_ai_suggestions` | Get AI-powered suggestions for a conversation |
234
+ | Tool | Description |
235
+ | --------------------------- | -------------------------------------------------------------------------------- |
236
+ | `generate_content` | Generate AI content for a contact conversation (initial email, follow-up, reply) |
237
+ | `generate_campaign_content` | Generate campaign-level content (email templates, subject lines, briefs) |
238
+ | `analyze_response` | Analyze inbound message sentiment, intent, and suggested action |
239
+ | `get_ai_usage` | Get AI generation usage limits |
241
240
 
242
241
  #### `generate_content` Parameters
243
242
 
244
- | Parameter | Type | Required | Description |
245
- | --- | --- | --- | --- |
246
- | `contact_id` | string (UUID) | Yes | Contact ID |
247
- | `conversation_id` | string (UUID) | Yes | Conversation ID |
248
- | `generation_type` | enum | Yes | `initial_email`, `follow_up`, `reply` |
249
- | `context` | string (≤2000) | No | Additional context for the AI |
243
+ | Parameter | Type | Required | Description |
244
+ | ----------------- | -------------- | -------- | ------------------------------------- |
245
+ | `contact_id` | string (UUID) | Yes | Contact ID |
246
+ | `conversation_id` | string (UUID) | Yes | Conversation ID |
247
+ | `generation_type` | enum | Yes | `initial_email`, `follow_up`, `reply` |
248
+ | `context` | string (≤2000) | No | Additional context for the AI |
250
249
 
251
250
  ### Discovery
252
251
 
253
- | Tool | Description |
254
- | --- | --- |
255
- | `search_contacts` | Search for contacts by criteria (location, keywords, sources) |
256
- | `search_campaign_contacts` | Run discovery scoped to a campaign's target profiles |
257
- | `get_discovery_job` | Get the status of a discovery job |
258
- | `list_discovery_sources` | List available discovery sources (Google Maps, social) |
252
+ | Tool | Description |
253
+ | -------------------------- | ------------------------------------------------------------- |
254
+ | `search_contacts` | Search for contacts by criteria (location, keywords, sources) |
255
+ | `search_campaign_contacts` | Run discovery scoped to a campaign's target profiles |
256
+ | `get_discovery_job` | Get the status of a discovery job |
257
+ | `list_discovery_sources` | List available discovery sources (Google Maps, social) |
259
258
 
260
259
  ### Channels
261
260
 
262
- | Tool | Description |
263
- | --- | --- |
264
- | `list_connected_accounts` | List your connected social accounts (Twitter, Instagram, LinkedIn) |
265
- | `check_channel_availability` | Check which channels are reachable for a contact |
266
- | `send_channel_message` | Send a message via a specific channel |
261
+ | Tool | Description |
262
+ | ---------------------------- | ------------------------------------------------------------------ |
263
+ | `list_connected_accounts` | List your connected social accounts (Twitter, Instagram, LinkedIn) |
264
+ | `check_channel_availability` | Check which channels are reachable for a contact |
265
+ | `send_channel_message` | Send a message via a specific channel |
267
266
 
268
267
  ### Analytics
269
268
 
270
- | Tool | Description |
271
- | --- | --- |
272
- | `get_analytics_overview` | Account-level analytics overview |
273
- | `get_campaign_analytics` | Per-campaign performance metrics |
269
+ | Tool | Description |
270
+ | ------------------------- | -------------------------------------- |
271
+ | `get_analytics_overview` | Account-level analytics overview |
272
+ | `get_campaign_analytics` | Per-campaign performance metrics |
274
273
  | `get_performance_metrics` | Aggregate performance across campaigns |
275
- | `get_response_time` | Response time distribution |
276
- | `get_contact_types` | Engagement broken down by contact type |
274
+ | `get_response_time` | Response time distribution |
275
+ | `get_contact_types` | Engagement broken down by contact type |
277
276
 
278
277
  ### Notifications
279
278
 
280
- | Tool | Description |
281
- | --- | --- |
282
- | `list_notifications` | List your notifications |
283
- | `update_notification` | Mark a notification as read or update its state |
284
- | `delete_notification` | Delete a notification |
285
- | `mark_all_notifications_read` | Mark every unread notification as read |
279
+ | Tool | Description |
280
+ | ----------------------------- | ----------------------------------------------- |
281
+ | `list_notifications` | List your notifications |
282
+ | `update_notification` | Mark a notification as read or update its state |
283
+ | `delete_notification` | Delete a notification |
284
+ | `mark_all_notifications_read` | Mark every unread notification as read |
286
285
 
287
286
  ### Billing
288
287
 
289
- | Tool | Description |
290
- | --- | --- |
288
+ | Tool | Description |
289
+ | ------------------- | --------------------------------- |
291
290
  | `get_billing_plans` | List available subscription plans |
292
291
 
293
292
  ### API Keys
294
293
 
295
- | Tool | Description |
296
- | --- | --- |
294
+ | Tool | Description |
295
+ | --------------------- | ------------------------------------------------- |
297
296
  | `get_current_api_key` | Get details about the currently authenticated key |
298
- | `list_api_keys` | List all API keys for the account |
299
- | `create_api_key` | Create a new API key (raw key returned once) |
300
- | `revoke_api_key` | Revoke (delete) an API key |
297
+ | `list_api_keys` | List all API keys for the account |
298
+ | `create_api_key` | Create a new API key (raw key returned once) |
299
+ | `revoke_api_key` | Revoke (delete) an API key |
301
300
 
302
301
  #### `create_api_key` Parameters
303
302
 
304
- | Parameter | Type | Required | Description |
305
- | --- | --- | --- | --- |
306
- | `name` | string (1-100) | Yes | Key name for identification |
307
- | `scopes` | string[] | No | Permission scopes (defaults to all scopes) — see [Scopes](#api-key--scopes) |
308
- | `expires_at` | string (ISO 8601) | No | Expiration date |
303
+ | Parameter | Type | Required | Description |
304
+ | ------------ | ----------------- | -------- | --------------------------------------------------------------------------- |
305
+ | `name` | string (1-100) | Yes | Key name for identification |
306
+ | `scopes` | string[] | No | Permission scopes (defaults to all scopes) — see [Scopes](#api-key--scopes) |
307
+ | `expires_at` | string (ISO 8601) | No | Expiration date |
309
308
 
310
309
  ### Webhooks
311
310
 
312
- | Tool | Description |
313
- | --- | --- |
314
- | `list_webhooks` | List all configured webhooks |
315
- | `create_webhook` | Create a new webhook endpoint |
316
- | `get_webhook` | Get a webhook by ID |
317
- | `update_webhook` | Update a webhook's URL, events, or active status |
318
- | `delete_webhook` | Delete a webhook |
319
- | `test_webhook` | Send a test event to a webhook URL |
320
- | `rotate_webhook_secret` | Rotate the signing secret (new secret returned once) |
321
- | `list_webhook_deliveries` | List delivery history for a webhook |
311
+ | Tool | Description |
312
+ | ------------------------- | ---------------------------------------------------- |
313
+ | `list_webhooks` | List all configured webhooks |
314
+ | `create_webhook` | Create a new webhook endpoint |
315
+ | `get_webhook` | Get a webhook by ID |
316
+ | `update_webhook` | Update a webhook's URL, events, or active status |
317
+ | `delete_webhook` | Delete a webhook |
318
+ | `test_webhook` | Send a test event to a webhook URL |
319
+ | `rotate_webhook_secret` | Rotate the signing secret (new secret returned once) |
320
+ | `list_webhook_deliveries` | List delivery history for a webhook |
322
321
 
323
322
  #### `create_webhook` Parameters
324
323
 
325
- | Parameter | Type | Required | Description |
326
- | --- | --- | --- | --- |
327
- | `url` | string (HTTPS URL) | Yes | Webhook endpoint URL — HTTPS only |
328
- | `events` | string[] (≥1) | Yes | Events to subscribe to — see [Webhook Events](#webhook-events) |
329
- | `description` | string (≤500) | No | Webhook description |
324
+ | Parameter | Type | Required | Description |
325
+ | ------------- | ------------------ | -------- | -------------------------------------------------------------- |
326
+ | `url` | string (HTTPS URL) | Yes | Webhook endpoint URL — HTTPS only |
327
+ | `events` | string[] (≥1) | Yes | Events to subscribe to — see [Webhook Events](#webhook-events) |
328
+ | `description` | string (≤500) | No | Webhook description |
330
329
 
331
330
  #### `update_webhook` Parameters
332
331
 
333
- | Parameter | Type | Required | Description |
334
- | --- | --- | --- | --- |
335
- | `id` | string (UUID) | Yes | Webhook ID |
336
- | `url` | string (HTTPS URL) | No | New endpoint URL |
337
- | `events` | string[] (≥1) | No | New events to subscribe to |
338
- | `active` | boolean | No | Enable or disable the webhook |
332
+ | Parameter | Type | Required | Description |
333
+ | --------- | ------------------ | -------- | ----------------------------- |
334
+ | `id` | string (UUID) | Yes | Webhook ID |
335
+ | `url` | string (HTTPS URL) | No | New endpoint URL |
336
+ | `events` | string[] (≥1) | No | New events to subscribe to |
337
+ | `active` | boolean | No | Enable or disable the webhook |
339
338
 
340
339
  ### Agent Credits
341
340
 
342
341
  > Credits apply only to agents authenticating via x402 (USDC on Base) or MPP (Stripe). API key users do not need credits.
343
342
 
344
- | Tool | Description |
345
- | --- | --- |
346
- | `get_credit_balance` | Check the agent credit balance |
343
+ | Tool | Description |
344
+ | ---------------------- | ----------------------------------------- |
345
+ | `get_credit_balance` | Check the agent credit balance |
347
346
  | `purchase_credit_pack` | Purchase a 5-credit pack via MPP ($12.00) |
348
347
 
349
348
  ## API Key & Scopes
@@ -352,50 +351,50 @@ Generate an API key at [iris-ai.dev/settings/api-keys](https://iris-ai.dev/setti
352
351
 
353
352
  API keys support fine-grained scopes per resource:
354
353
 
355
- | Scope | Access |
356
- | --- | --- |
357
- | `campaigns:read` / `campaigns:write` | Read or modify campaigns |
358
- | `contacts:read` / `contacts:write` | Read or modify contacts |
359
- | `conversations:read` / `conversations:write` | Read conversations or send messages |
360
- | `messages:read` / `messages:write` | Read the queue, approve/reject/send |
361
- | `ai:read` / `ai:write` | Read AI usage or trigger generations |
362
- | `discovery:read` / `discovery:write` | Read sources or run discovery jobs |
363
- | `analytics:read` | Read analytics dashboards |
364
- | `billing:read` | Read billing plans |
365
- | `channels:read` / `channels:write` | Read connected accounts or send via channels |
366
- | `notifications:read` / `notifications:write` | Read or modify notifications |
367
- | `webhooks:read` / `webhooks:write` | Read or manage webhook subscriptions |
368
- | `keys:read` / `keys:write` | Read or manage API keys |
354
+ | Scope | Access |
355
+ | -------------------------------------------- | -------------------------------------------- |
356
+ | `campaigns:read` / `campaigns:write` | Read or modify campaigns |
357
+ | `contacts:read` / `contacts:write` | Read or modify contacts |
358
+ | `conversations:read` / `conversations:write` | Read conversations or send messages |
359
+ | `messages:read` / `messages:write` | Read the queue, approve/reject/send |
360
+ | `ai:read` / `ai:write` | Read AI usage or trigger generations |
361
+ | `discovery:read` / `discovery:write` | Read sources or run discovery jobs |
362
+ | `analytics:read` | Read analytics dashboards |
363
+ | `billing:read` | Read billing plans |
364
+ | `channels:read` / `channels:write` | Read connected accounts or send via channels |
365
+ | `notifications:read` / `notifications:write` | Read or modify notifications |
366
+ | `webhooks:read` / `webhooks:write` | Read or manage webhook subscriptions |
367
+ | `keys:read` / `keys:write` | Read or manage API keys |
369
368
 
370
369
  ### Recommended Scopes by Use Case
371
370
 
372
- | Use Case | Recommended Scopes |
373
- | --- | --- |
374
- | AI agents / MCP | `campaigns:read`, `campaigns:write`, `contacts:read`, `contacts:write`, `messages:read`, `messages:write`, `ai:read`, `ai:write`, `conversations:read`, `conversations:write` |
375
- | Read-only monitoring | `*:read` scopes only |
376
- | Webhook management | `webhooks:read`, `webhooks:write` |
377
- | Discovery automation | `discovery:read`, `discovery:write`, `contacts:write`, `campaigns:read` |
371
+ | Use Case | Recommended Scopes |
372
+ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
373
+ | AI agents / MCP | `campaigns:read`, `campaigns:write`, `contacts:read`, `contacts:write`, `messages:read`, `messages:write`, `ai:read`, `ai:write`, `conversations:read`, `conversations:write` |
374
+ | Read-only monitoring | `*:read` scopes only |
375
+ | Webhook management | `webhooks:read`, `webhooks:write` |
376
+ | Discovery automation | `discovery:read`, `discovery:write`, `contacts:write`, `campaigns:read` |
378
377
 
379
378
  ## Webhook Events
380
379
 
381
- | Event | Description |
382
- | --- | --- |
383
- | `campaign.created` | New campaign created |
384
- | `campaign.updated` | Campaign details changed |
385
- | `campaign.deleted` | Campaign soft-deleted |
386
- | `contact.created` | New contact added |
387
- | `contact.updated` | Contact details changed |
388
- | `contact.deleted` | Contact soft-deleted |
389
- | `conversation.created` | New conversation started |
390
- | `conversation.reply_received` | Inbound reply received |
391
- | `message.sent` | Outbound message sent |
392
- | `message.delivered` | Message confirmed delivered |
393
- | `message.opened` | Email opened (filtered for bots) |
394
- | `message.bounced` | Message bounced |
395
- | `message.approved` | Message approved in the queue |
396
- | `message.rejected` | Message rejected in the queue |
397
- | `discovery.completed` | Discovery job finished successfully |
398
- | `discovery.failed` | Discovery job failed |
380
+ | Event | Description |
381
+ | ----------------------------- | ----------------------------------- |
382
+ | `campaign.created` | New campaign created |
383
+ | `campaign.updated` | Campaign details changed |
384
+ | `campaign.deleted` | Campaign soft-deleted |
385
+ | `contact.created` | New contact added |
386
+ | `contact.updated` | Contact details changed |
387
+ | `contact.deleted` | Contact soft-deleted |
388
+ | `conversation.created` | New conversation started |
389
+ | `conversation.reply_received` | Inbound reply received |
390
+ | `message.sent` | Outbound message sent |
391
+ | `message.delivered` | Message confirmed delivered |
392
+ | `message.opened` | Email opened (filtered for bots) |
393
+ | `message.bounced` | Message bounced |
394
+ | `message.approved` | Message approved in the queue |
395
+ | `message.rejected` | Message rejected in the queue |
396
+ | `discovery.completed` | Discovery job finished successfully |
397
+ | `discovery.failed` | Discovery job failed |
399
398
 
400
399
  Webhook deliveries are signed with HMAC-SHA256 using the secret returned from `create_webhook` / `rotate_webhook_secret`. Verify the signature in the `X-Iris-Signature` header before trusting the payload.
401
400
 
package/dist/cli-utils.js CHANGED
@@ -27,5 +27,8 @@ export function getFormat(opts) {
27
27
  return opts.format || "json";
28
28
  }
29
29
  export function parseList(value) {
30
- return value.split(",").map((s) => s.trim()).filter(Boolean);
30
+ return value
31
+ .split(",")
32
+ .map((s) => s.trim())
33
+ .filter(Boolean);
31
34
  }
package/dist/cli.js CHANGED
@@ -24,7 +24,19 @@ const program = new Command()
24
24
  .option("--json", "output as JSON (default)")
25
25
  .option("--format <fmt>", "output format: json, table", "json");
26
26
  try {
27
- const client = new IrisClient();
27
+ // Lazy client: defer the IrisClient constructor (which throws when
28
+ // IRIS_API_KEY is missing) until a command actually issues an HTTP call.
29
+ // `iris -V`, `iris --help`, `iris ai --help` etc. never touch the proxy,
30
+ // so they exit cleanly without an API key set.
31
+ let realClient = null;
32
+ const client = new Proxy({}, {
33
+ get(_target, prop, receiver) {
34
+ if (!realClient)
35
+ realClient = new IrisClient();
36
+ const value = Reflect.get(realClient, prop, receiver);
37
+ return typeof value === "function" ? value.bind(realClient) : value;
38
+ },
39
+ });
28
40
  registerCampaignCommands(program, client);
29
41
  registerContactCommands(program, client);
30
42
  registerMessageCommands(program, client);
@@ -1,8 +1,7 @@
1
1
  import { formatOutput, handleError, getFormat, parseList } from "../cli-utils.js";
2
2
  export function registerAICommands(program, client) {
3
3
  const ai = program.command("ai").description("AI content generation and analysis");
4
- ai
5
- .command("generate")
4
+ ai.command("generate")
6
5
  .description("generate AI content (email draft, subject line, etc.)")
7
6
  .requiredOption("-t, --generation-type <type>", "generation type (e.g. email_draft, subject_line)")
8
7
  .requiredOption("--tone <tone>", "tone (e.g. professional, casual, friendly)")
@@ -28,8 +27,7 @@ export function registerAICommands(program, client) {
28
27
  handleError(error);
29
28
  }
30
29
  });
31
- ai
32
- .command("generate-campaign")
30
+ ai.command("generate-campaign")
33
31
  .description("generate campaign-level content (email sequence, templates)")
34
32
  .requiredOption("-n, --campaign-name <name>", "campaign name")
35
33
  .requiredOption("-o, --objective <text>", "campaign objective")
@@ -52,8 +50,7 @@ export function registerAICommands(program, client) {
52
50
  handleError(error);
53
51
  }
54
52
  });
55
- ai
56
- .command("analyze")
53
+ ai.command("analyze")
57
54
  .description("analyze a response in a conversation")
58
55
  .requiredOption("--conversation-id <id>", "conversation ID")
59
56
  .requiredOption("--email-content <text>", "email content to analyze")
@@ -69,8 +66,7 @@ export function registerAICommands(program, client) {
69
66
  handleError(error);
70
67
  }
71
68
  });
72
- ai
73
- .command("usage")
69
+ ai.command("usage")
74
70
  .description("get AI generation usage stats for the current billing period")
75
71
  .action(async () => {
76
72
  try {
@@ -81,17 +77,4 @@ export function registerAICommands(program, client) {
81
77
  handleError(error);
82
78
  }
83
79
  });
84
- ai
85
- .command("suggestions")
86
- .description("get AI-suggested replies for a conversation")
87
- .requiredOption("--conversation-id <id>", "conversation ID")
88
- .action(async (opts) => {
89
- try {
90
- const result = await client.get("/ai/suggestions", { conversation_id: opts.conversationId });
91
- formatOutput(result, getFormat(program.opts()));
92
- }
93
- catch (error) {
94
- handleError(error);
95
- }
96
- });
97
80
  }