---
id: WF-COM-CAT-013
title: Generate a batch of promotion codes
trigger: >-
  Staff generate a bounded batch of claim codes. A directly invoked worker resumes from saved progress,
  records partial failures and supports CSV export.
product_area: commerce
module: catalog
revision: 1
status: draft
design_intent: confirmed
implementation_evidence: observed
inference: []
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
  - CAP-COM-CAT-015
moves:
  - STATE-COM-CAT-009
crosses:
  - SVC-COM-CAT-promotions
idempotency: >-
  A supplied request key determines the workspace batch key. Repeats continue unfinished work or return final
  batches; no key means a new batch. Progress always comes from saved codes.
concurrency: >-
  Chunk writes lock and recount the batch before cutting to remaining quantity. Separate batches can run
  independently; the worker has no reserved-concurrency cap.
hops:
  - 'n': 1
    actor_or_service: Promotions API
    action: >-
      Validate wildcard space, ambiguity exclusions, minimum wildcard count, requested quantity and shared
      code defaults.
    anchor: 'services-commerce:modules/promotions/services/issuance/code-template.ts'
    seam: sync
    on_failure: Reject an insufficient template space or invalid quantity with readable feedback.
    left_inconsistent: No batch is retained.
    observer: The requesting person or service receives the result and any returned refusal.
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/unit/issuance/code-template.spec.js#refuses the same template
      for 27 codes, saying what it can make and what was asked
  - 'n': 2
    actor_or_service: Promotions API
    action: >-
      Lock the parent promotion for reading, validate claim kind and dates, then insert or reuse the requested
      batch.
    anchor: >-
      services-commerce:modules/promotions/services/persistence/promotion-code-issuances.ts:requestPromotionCodeIssuance
    seam: sync
    on_failure: Reject automatic or retired campaigns and invalid parent-bounded dates.
    left_inconsistent: No new batch is retained.
    observer: The requesting person or service receives the result and any returned refusal.
    provenance: documented
    source: 'services-commerce:modules/promotions/services/persistence/promotion-code-issuances.ts'
  - 'n': 3
    actor_or_service: Promotions API to issuance worker
    action: >-
      Invoke the worker asynchronously with workspace and batch context; without a configured worker, execute
      locally in the request.
    anchor: 'services-commerce:modules/promotions/services/issuance/issuance-starter.ts:startIssuanceJob'
    seam: event
    on_failure: Mark invocation failure on the batch; Lambda retries only executions that actually fail.
    left_inconsistent: A failed batch remains available for staff inspection.
    observer: The requesting person or service receives the result and any returned refusal.
    provenance: documented
    source: 'services-commerce:modules/promotions/services/issuance/issuance-workflow.ts:requestCodeIssuance'
  - 'n': 4
    actor_or_service: Issuance worker
    action: >-
      Recount saved codes, generate missing codes in chunks up to five hundred, bound conflict retries and
      record completion or failure.
    anchor: 'services-commerce:modules/promotions/services/issuance/issuance-job.ts:runPromotionCodeIssuance'
    seam: sync
    on_failure: Retain saved codes and a batch failure explanation when generation stops.
    left_inconsistent: A failed batch can contain active codes below the requested quantity.
    observer: Request feedback and service logs expose failures where the caller returns them.
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/unit/issuance/issuance-job.spec.js#continues from the codes
      that exist after a crash, making only the missing ones
  - 'n': 5
    actor_or_service: Promotions API
    action: >-
      On a stale requested or running read, restart generation; export saved codes using spreadsheet-safe CSV
      cells.
    anchor: 'services-commerce:modules/promotions/services/issuance/issuance-workflow.ts:readCodeIssuance'
    seam: sync
    on_failure: Log a failed restart and return the current batch rather than failing its read.
    left_inconsistent: The batch awaits another recovery request or stale read.
    observer: Request feedback and service logs expose failures where the caller returns them.
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/unit/issuance/issuance-request-csv.spec.js#prefixes cells
      starting with = + - @ with a single quote
branches: []
waits:
  - >-
    The worker has a two-minute execution timeout; a requested or running batch becomes restartable by a read
    after five minutes without a write.
evidence:
  - >-
    services-commerce at c8b66a98: modules/promotions/services/issuance/*.ts;
    services/persistence/promotion-code-issuances.ts; aws/lambda/ISSUANCE.ts;
    aws/logic/promotion-code-issuance-worker.lambda.ts; docs/milestones/COMM-M1b-promotions-and-codes.md
    (D-R2).
  - 'react-console at baf6dae3: components/commerce/promotions/generate-codes-dialog.tsx, issuances-tab.tsx.'
controls: [TC-IAM-006, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-006, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-CRY-001, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-DAT-005, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-AVL-004, TC-MON-001, TC-MON-002, TC-MON-004, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002]
---

# Generate a batch of promotion codes

## What It Does

Staff generate a bounded batch of claim codes. A directly invoked worker resumes from saved progress, records partial failures and supports CSV export.

## The Flow

```mermaid
sequenceDiagram
  participant S as Staff
  participant A as Promotions API
  participant W as Issuance worker
  participant D as Database
  S->>A: template, quantity, defaults (idempotency key)
  A->>A: judge template and quantity
  A->>D: write batch requested (or find it)
  A-)W: invoke asynchronously
  A-->>S: batch requested
  W->>D: running; chunks of 500 under the batch lock
  W->>D: completed or failed with message
  S->>A: read batch (restarts a stale one)
```

## When Something Fails

Reject an insufficient template space or invalid quantity with readable feedback. No batch is retained. Reject automatic or retired campaigns and invalid parent-bounded dates. No new batch is retained. Mark invocation failure on the batch; Lambda retries only executions that actually fail. A failed batch remains available for staff inspection. Retain saved codes and a batch failure explanation when generation stops. A failed batch can contain active codes below the requested quantity. Log a failed restart and return the current batch rather than failing its read. The batch awaits another recovery request or stale read.

## Open Design Questions

The implemented recovery and concurrency behaviour above defines the current boundary. Any change to transaction scope or product authority requires a recorded decision.
