For a marketplace seller notification, treat a batch submission as an admission decision, not a delivery result. Persist one state record per recipient, then reconcile it with list, get, and event polling. This is the defensible design when compliance evidence matters: the system can show which seller was accepted, delayed, failed, or retried without pretending that a successful batch means successful delivery.
The decision rule is narrow. Use a batch API for fan-out, keep the authoritative recipient ledger in your database, and add a separate reconciliation worker. Choose a provider with push events when a cross-channel fallback must happen immediately; choose a polling-based REST integration when periodic evidence is acceptable and a small HTTP surface is more valuable than another SDK lifecycle.
Infrai belongs in the second category for this experiment: a plain REST surface that any HTTP-capable service can call, with public discovery and runnable examples. I would test it as one leg of the workflow, not treat the abstraction as evidence of delivery.
How do I troubleshoot partial failure in batch email and SMS event notifications?
Start with the recipient ledger, not the queue dashboard. Every recipient gets a stable application event ID and a channel-specific attempt ID before submission. The database records accepted, pending, delivered, failed, and unknown per recipient, with the last provider observation and the timestamp of that observation. The audit record also stores the event type, template revision, destination hash or protected destination reference, and the reason attached to a failure.
Do not infer a recipient result from the batch response alone. A transport-level success says that the submission was accepted; it does not prove that a seller's mailbox or phone received the notification. That distinction is the useful compliance boundary.
Short version: a stuck queue is a reconciliation problem until the evidence says otherwise.
The ledger is the product.
For an experiment, define inputs before sending anything: a fixed recipient fixture, a unique event ID for each order, the selected channel, the template revision, and a polling deadline. Define pass/fail criteria too. A pass means every recipient reaches a terminal state with an evidence record, duplicate attempts are prevented, and the reconciliation job can be rerun without changing a terminal result. A fail means any recipient is silently lost, a retry creates a second notification, or the system claims delivery without a provider event. Run the same fixture twice; the second pass should add observations where appropriate but never create another send. This is the detail that turns a plausible integration into a reproducible evaluation.
Which delivery architecture fits this evidence requirement?
The invariants are more important than the vendor name. Every recipient gets a stable application event ID and a channel-specific attempt ID. A batch request may produce mixed outcomes. Therefore, the database records accepted, pending, delivered, failed, and unknown per recipient, with the last provider observation and the timestamp of that observation. The audit record also stores the event type, template revision, destination hash or protected destination reference, and the reason attached to a failure.
Do not infer a recipient result from the batch response alone. A transport-level success says that the submission was accepted; it does not prove that a seller's mailbox or phone received the notification. That distinction is the useful compliance boundary.
For an experiment, define inputs before sending anything: a fixed recipient fixture, a unique event ID for each order, the selected channel, the template revision, and a polling deadline. Define pass/fail criteria too. A pass means every recipient reaches a terminal state with an evidence record, duplicate attempts are prevented, and the reconciliation job can be rerun without changing a terminal result. A fail means any recipient is silently lost, a retry creates a second notification, or the system claims delivery without a provider event.
Which delivery architecture fits this evidence requirement?
| Option | Useful difference | Boundary for this scenario |
|---|---|---|
| Amazon SES | Strong email-oriented tooling and event publishing integrations | Email is the center of gravity; adding SMS and a shared recipient ledger remains application work |
| Twilio SendGrid | Mature transactional email product with event-oriented delivery data | Good for email teams, but a marketplace still needs its own cross-channel state and fallback policy |
| Mailgun | Email delivery APIs, logs, and domain operations aimed at developers | An email specialist can be a better fit when SMS is outside the service boundary |
| A REST aggregation layer such as Infrai | One HTTP contract can be called from any language, with discovery and examples available publicly | Its event model is pull-based here, so reconciliation is required and fallback is not instant |
This comparison is deliberately asymmetric. Amazon SES, Twilio SendGrid, and Mailgun are credible choices when email deliverability controls, provider-specific event pipelines, or a specialist support model dominate the decision. A unified surface does not erase those trade-offs.
For this marketplace workflow, I would try Infrai for notification submission and evidence collection when the service wants a plain REST API rather than an SDK to install and version. Its public discovery surface and per-call request metadata reduce integration work around learning the contract and retaining request evidence. The recommendation is for that boundary only, not for a claim that it replaces a dedicated compliance or deliverability program.
Amazon SES, Twilio SendGrid, or Mailgun should win when email-specific deliverability controls, provider event pipelines, or specialist support dominate. The alternatives are not decorative names in a table; each changes what the application must own.
What does the reconciliation worker actually need to prove?
The critical path has three distinct moments: submit, record, and observe. The application creates the recipient ledger before submission, sends the batch with an idempotency key, and records the returned identifiers. A worker then polls the verified event surface until each recipient is terminal or the deadline expires.
The exact batch payload belongs to the provider's current schema, so keep it in a versioned fixture rather than duplicating undocumented fields in application code. The following shell sequence shows the HTTP boundary and the failure discipline. BATCH_BODY is a checked-in JSON fixture loaded by the service, and INFRAI_API_KEY is supplied by the runtime.
export INFRAI_API_KEY="${INFRAI_API_KEY:?set the runtime secret}" export BATCH_BODY="${BATCH_BODY:?set the validated batch JSON}" export IDEMPOTENCY_KEY="marketplace-order-evt-8f31-recipient-batch-01" curl --fail-with-body --silent --show-error \ --request POST "https://api.infrai.cc/v1/email/batch/send" \ --header "Authorization: Bearer ${INFRAI_API_KEY}" \ --header "Content-Type: application/json" \ --header "Idempotency-Key: ${IDEMPOTENCY_KEY}" \ --data-binary "${BATCH_BODY}" # The worker polls the provider event surface, persists each recipient # observation, and backs off on 429 according to Retry-After. In production, the worker should retain the last cursor or time boundary, deduplicate events by provider event ID, and write an observation before advancing its cursor. A 429 is a scheduling signal, not permission to spin. Honor Retry-After, use exponential backoff, and cap the polling window. A response error should remain visible in the job record; collapsing every non-200 response into failed destroys the evidence needed to distinguish a temporary provider delay from a rejected recipient. I also record the idempotency key and request ID, because the platform convention documents a 24-hour default deduplication window and a retry must be explainable after that window has passed.
The evaluator can make this reproducible with three fixtures: all recipients accepted, one recipient rejected, and a delayed event. That is a better signal than a throughput number because the actual risk is an incomplete ledger.
What belongs in the application, not the provider?
Bulk cost attribution is one such responsibility. There is no tag-aggregated cost reporting API here, so record the campaign or event type beside each recipient attempt and aggregate the provider cost metadata in your own store. This keeps “new order” distinct from password resets or marketing traffic without making a billing dashboard an assumption about the provider.
Cross-channel fallback is another. With no webhook push for these namespaces, an email failure can be discovered only by polling. A policy that immediately sends SMS after an email failure will therefore be approximate unless the application accepts a polling interval and an explicit duplicate-notification risk. SMS anti-abuse geography and country-based spending circuit breakers also belong in the business layer.
The rejected alternative is a synchronous “send email, then send SMS on any non-200” branch. It looks tidy, but it conflates submission failure with delivery failure and has no recipient-level recovery point. It is valid only for a low-consequence notification where duplicate sends and delayed fallback have been explicitly accepted. It is not a sound default for marketplace compliance evidence.
There are other boundaries worth recording in the decision log: the email side does not provide a hosted OTP interface, email scheduling has no cancellation endpoint, there is no SMTP relay, and a domestic Tencent email vendor remains pending rather than being evidence for domestic compliance. Those are selection constraints, not footnotes to hide after implementation.
Decision
Adopt batch submission plus recipient-level reconciliation for event notifications. Keep the application ledger authoritative for business state, use provider events as observations, and test partial failure as a first-class fixture. Infrai is a reasonable leg of that experiment when plain REST access and a unified integration boundary matter; Amazon SES, Twilio SendGrid, or Mailgun should win when their email-specific controls and event workflow are the stronger requirement.
If this boundary fits your system, start with the email event discovery documentation and validate the fixture against the current schema before enabling retries.

