Campaign Recipients¶
Enums: CampaignRecipientStatus and CampaignRecipientSkipReason in app/core/models/campaign_recipients.py
States¶
graph LR
Pending --> ProofingStaged
Pending --> AddressEnrichable
Pending --> Skipped
Pending --> Holdout
ProofingStaged --> Proofing
Proofing --> Proofed
Proofing --> Error
Proofed --> SendingStaged
Proofed --> Scheduled
SendingStaged --> Sending
Sending --> Sent
Sending --> SendingStaged
Sending --> Error
Sent --> Received
Scheduled --> Sent
AddressEnrichable --> AddressEnriching
AddressEnriching --> Pending
AddressEnriching --> Skipped
classDef starting fill:#96d0ff;
classDef ending fill:#ffd6d6;
classDef static fill:#dccbff;
classDef paused fill:#fff2a8;
class Pending starting;
class Received ending;
class Holdout,Skipped,Error,Archived,Blacklisted static;
class AddressEnrichable,AddressEnriching paused;
Pending¶
Synced from integration. Ready for proofing.
ProofingStaged¶
Queued in SQS awaiting proof generation.
Proofing¶
Proof generation in progress (image personalization).
Proofed¶
Proof generated. Ready for mailing.
SendingStaged¶
Queued in SQS awaiting mailpiece creation.
Sending¶
Mailpiece creation in progress.
Sent¶
Mailpiece submitted to printer.
Received¶
Mailpiece delivered (tracked via printer webhooks).
Scheduled¶
Awaiting future mail date. Frozen until campaign completes.
Holdout¶
Control group member. Not mailed intentionally.
Skipped¶
Validation failed (see skip reason).
AddressEnrichable¶
Missing address but eligible for enrichment. When address enrichment is enabled on the campaign and the recipient has no address1, they get AddressEnrichable instead of being Skipped with NoAddress. This gates them into the enrichment flow without losing the recipient.
AddressEnriching¶
Address enrichment in progress via Faraday.
Error¶
Fatal error in proof/mailer/queue.
Archived¶
Excluded from campaign (legacy).
Blacklisted¶
On organization blocklist.
Skip Reasons¶
| Reason | Meaning |
|---|---|
| NoAddress | No address1 provided, or enrichment failed. |
| International | Country check or address validation failed. |
| CapExceeded | Billing recipient cap reached. |
| EnrichmentFailed | Faraday enrichment returned no result. |
| PrinterApprovalFailed | Printer-specific rules rejected recipient. |
Recipient Validation¶
Recipients are validated at two stages during sync:
Data Validation (sync time)¶
RecipientDataValidator in campaign_recipients.py is a Pydantic BaseModel that validates all incoming recipient data:
- Whitespace stripping on all string fields
- Required non-empty fields:
address1,zip,country - Name rules: at least one of
first_nameorlast_namerequired; asterisks rejected in names - Recipients that fail validation are
Skippedwith an appropriate reason
Printer Approval (pre-mailing)¶
Per-printer Pydantic approval models validate recipients against printer-specific rules before mailing:
LobRecipientApproval— LOB-specific address/format requirementsIntelliprintRecipientApproval— IntelliPrint-specific requirements
Recipients that fail approval are Skipped with reason PrinterApprovalFailed. Validation errors are stored in recipient.json["approval_errors"] for debugging.
See core/integrations/printers/approval_rules.py for the rule definitions.
Key files¶
- Pipeline:
celery/proofer.py,celery/mailer.py,methods/runner.py - Validation:
methods/campaigns/recipient_sync.py - Enrichment:
celery/address_enrichment.py - Holdouts:
methods/recipients.py
app.core.models.campaign_recipients.ErrorMessages
¶
Error messages set on campaign recipients.
BILLING_NOT_READY = 'Org billing is not ready to send'
class-attribute
instance-attribute
¶
Preflight error.
Raised by preflight_campaign_billing when no usable billing path
exists for the org (no Stripe customer, no active subscription, no
legacy SubscriptionItem, or legacy price drift). Fails closed at the
proofing/sending stage so we don't burn Pixelixe credits or print a
mailpiece against an un-billable org.
COULD_NOT_GENERATE_CREATIVE = 'Could not generate creative for recipient'
class-attribute
instance-attribute
¶
Printer error (Taylor).
This occurs when:
- Image personalization service is unavailable
- Template data is incomplete or invalid
- Creative generation timeout
- Missing front or back image assets
- Pixelixe personalization API errors
FAILED_SUBMISSION_TO_MAILING_QUEUE = 'Failed submission to Mailing Queue'
class-attribute
instance-attribute
¶
Mailing error.
This can happen when:
- Mailing queue is down or unreachable
- Authentication issues with mailing queue
- Invalid mailing data format
- Mailing queue quota exceeded
FAILED_SUBMISSION_TO_PRINTER = 'Failed submission to Printer'
class-attribute
instance-attribute
¶
Printer error (Taylor).
Typical causes:
- Printer service API is down or unreachable
- Authentication issues with printer service
- Invalid mail piece data format
- Print service quota exceeded
- Taylor/IntelliPrint/LOB API rejecting the submission
FAILED_SUBMISSION_TO_PROOFING_QUEUE = 'Failed submission to Proofing Queue'
class-attribute
instance-attribute
¶
Proofing error.
This can happen when:
- Proofing queue is down or unreachable
- Authentication issues with proofing queue
- Invalid proofing data format
- Proofing queue quota exceeded
FAILED_TO_APPLY_DISCOUNT_CODE = 'Failed to apply discount code'
class-attribute
instance-attribute
¶
Proofing error.
This can happen when:
- Discount code generation service is down
- Invalid discount code configuration
- External e-commerce platform API issues
- Shopify integration failures
FAILED_TO_MAKE_MAIL_PIECE = 'Failed to make mail piece for this recipient'
class-attribute
instance-attribute
¶
Mailing error.
Common causes include:
- Print service API failures (LOB, IntelliPrint, Stannp)
- Invalid recipient address data
- Mail piece template generation issues
- Network connectivity problems with printer services
FAILED_TO_MAKE_PROOF = 'Failed to make proof for this recipient'
class-attribute
instance-attribute
¶
Proofing error.
- Image generation or personalization fails
- Template rendering encounters issues
- External proof generation service is unavailable
- Pixelixe API is down or returns errors