Version
DESTRUCTIVE

bulk_generate_access_codes

Queue a bulk access-code generation batch. Returns a job_id immediately — poll get_job_status for completion.

Bulk-generate access codes for a plan. The underlying job can take minutes for large batches, so the tool returns a job_id immediately; clients poll get_job_status until status=completed. The access_code.generated event emits once per batch when the worker finishes.

Billing model: generation is free. The Stripe metered overage triggers on redemption when the creator's tier free allotment is exhausted. The response embeds a preview so callers can still surface the worst-case cost ("if all N get redeemed"). For a cost-only query, call preview_access_code_cost.

Delivery goes through the creator's connector chat (the Telegram bot today), never through this tool's response. export_type must be file (a CSV sent to the creator) or messages (one forwardable message per code) — both require the creator account to have a linked connector identity. Agents that need programmatic access must also call list_access_codes after the batch completes.

The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.

The REST endpoint and this tool share one action, so validation, permissions and events are identical.

Fires one event

Delivered to every endpoint subscribed to it once the change is made.

Annotations

DestructiveOpen world

A client that honours annotations asks a person before running it. It reaches beyond Subscriby: a connector, a provider or a member.

Arguments

plan_id*string

UUID of the plan to attach the generated codes to.

quantity*integer

How many access codes to generate (1-1000).

export_type*string

How the job delivers the codes to the creator — file (CSV via bot) or messages (one per code via bot). Required.

expiry_preset*string

Required. One of no_expiry, 1_day, 1_week, 1_month, 1_year, custom.

custom_expiry_amountintegeroptional

When expiry_preset=custom, the amount of custom_expiry_type units. Required when preset=custom.

custom_expiry_typestringoptional

When expiry_preset=custom, one of days/weeks/months/years. Required when preset=custom.

consentbooleanoptional

Required and must be true when export_type=messages — acknowledges that one message per code will be sent to the creator's connector chat.

What it returns

{  "data": {    "job_id": "0a4e7b96-c358-4d12-9f6b-25a8013ce74f",    "status": "queued",    "enqueued_at": "2026-05-18T10:05:00Z",    "plan_id": "c4e82f16-93a7-4d5b-b81c-6e0f27a94d3b",    "quantity": 50,    "preview": {      "free_remaining": 3,      "chargeable_quantity": 47,      "cost": "1.41"    }  }}

Poll get_job_status with the returned job_id until status is completed or failed. It always reaches one of the two.

On completion the job's result carries the batch summary:

{
  "plan_id": "c4e82f16-93a7-4d5b-b81c-6e0f27a94d3b",
  "plan_name": "Premium Monthly",
  "count": 50,
  "export_type": "file",
  "expires_at": null,
  "delivered": true,
  "delivery_error": null
}

Generation and delivery succeed separately

The codes are written first, then handed to the creator over their connector. If that delivery fails — the bot is disconnected, the platform is down — the job still completes: the codes exist and are redeemable, and access_code.generated still fires. delivered goes false and delivery_error carries the reason, so you can tell the creator their CSV never arrived without anyone concluding the batch failed and generating it twice.

How it fails

VALIDATION_FAILED

quantity out of range, unknown export_type, missing consent when export_type=messages, missing custom_expiry_amount / custom_expiry_type when expiry_preset=custom, unknown expiry_preset, a batch for the same plan already in flight, or the underlying Action rejects the inputs.

RESOURCE_NOT_FOUND

unknown plan_id, or the plan belongs to a team outside the token's scope.

AUTHENTICATION_REQUIRED

no authenticated user on the request.

TOKEN_MISSING_ABILITY

token lacks project-access-code:create.

One batch per plan at a time

Queueing a second batch for a plan whose previous one has not reported an outcome is refused with VALIDATION_FAILED, and the message names the live job_id to poll instead.

The worker deduplicates by creator, project and plan, so a second batch inside that window would be dropped without a word — handing you a job id for work that was never going to run. Refusing up front is the honest version of the same constraint.

How is this guide?

Last updated on