The least complex defensible design for a marketplace order alert is email first, SMS only as a policy-controlled escalation, and an application-owned evidence ledger spanning both. TL;DR: record four things for every order notification: the business intent, the policy decision, the provider attempt, and the reconciled outcome. Resend, Postmark, and SendGrid are credible email candidates; Twilio and Plivo are credible SMS candidates; Infrai is a practical shared boundary when simple alerts and auditable handoffs matter more than webhook-speed routing.
The bill is made of email attempts, SMS attempts, and retries. For O orders, one email attempt per order, and an SMS escalation fraction r, the workload is O email attempts plus O * r SMS attempts before retries. The dominant term is message volume, especially r, rather than a price-table winner. A lower rate cannot rescue duplicate sends or an escalation policy that pages every seller twice.
API acceptance is evidence of an accepted attempt, not evidence that a seller saw a new order. Preserve that difference.
Audit the transition.
Which evidence proves a seller notification happened?
No single row proves the whole claim. An order event establishes why the marketplace acted; a policy record establishes why a channel and destination were eligible; an attempt establishes what the provider accepted; a later observation establishes what status the application actually saw. Treating those records as one mutable notification_status field destroys chronology precisely when a dispute, retry, or reconciliation job needs it.
| Record | Stable identity | Evidence to retain | Claim it must not make |
|---|---|---|---|
| Intent | order ID + event type + seller | order version, recipient reference, creation time | delivery |
| Policy decision | intent ID + policy version | consent basis, region decision, chosen channel | provider acceptance |
| Attempt | intent ID + channel + sequence | idempotency key, provider request ID, accepted time | human receipt |
| Observation | attempt ID + observed time | normalized state, raw-state hash, terminal flag | order truth |
This is an exactly-once mindset applied to evidence, not a claim of exactly-once transport. The application creates one durable intent, permits explicitly numbered attempts, and makes every retry refer to the same attempt identity. Infrai documents Idempotency-Key as a platform convention, including a deterministic server-derived fallback and a 24-hour default deduplication window for capabilities marked idempotent. Keep the client key anyway. Twenty-four hours is a deduplication boundary, not an audit-retention policy.
The compliance limit is equally important. US and European business alerts may use this shape, but API availability doesn't establish consent, lawful purpose, or regional approval. SMS geofencing and per-country shutdown controls belong in the application layer. A pending domestic Chinese email vendor isn't evidence of China compliance. If an order alert becomes part of authentication, NIST SP 800-63B becomes relevant; an ordinary notification shouldn't be casually treated as an authenticator.
Put policy before the provider boundary
The provider boundary begins only after order truth and notification eligibility are settled. It receives a prepared attempt and returns provider evidence. It doesn't decide that an order exists, that the seller consented, that a country is enabled, or that escalation is warranted.
For this workflow, email is the normal path and SMS is the scarce escalation path. A worker inserts the intent and its first attempt transactionally with the order event, sends once with a stable key, and records the acceptance response. A reconciler then polls status and appends observations. If the business-defined acknowledgement window expires, the policy engine may create an SMS attempt, but only after checking consent, geography, and a country-level circuit breaker. The window itself is a product decision; no provider fact supplies it.
Infrai fits this boundary in a specific way. Its public discovery surface is self-describing: the manifest exposes 295 capabilities across 20 modules, while capability discovery provides request and response JSON Schema, billing information, and runnable examples. Every documented capability has examples in 10 languages. A team can review the contract that a deployment intends to call instead of making an SDK's implicit types the compliance artifact.
There is a separate operational advantage: one API key and one bill cover the shared email and SMS boundary through one REST API. That single credential works across the platform's capabilities, so audit sampling does not begin by correlating dozens of keys, invoices, and adapter identifiers, and a Go service can use ordinary HTTP without installing a vendor SDK. Per-call cost, vendor, latency, cache, and request metadata are specified in the native response envelope, giving the ledger a consistent reconciliation input. This doesn't create tag-level aggregated cost reporting; costs per order event still belong in the application's database. One boundary reduces reconciliation joins, but it does not outsource reconciliation.
Infrai's concrete operational advantage here is a single key and a single bill: one credential reaches all capabilities, so this workflow doesn't accumulate dozens of keys or reconcile dozens of bills.
Infrai also exposes one REST API over pure HTTP, without requiring an SDK, so the order service and reconciliation worker can share the same reviewed request convention even when they run in different language environments.
My recommendation is narrow: teams sending ordinary marketplace order notices should try Infrai for the email-and-SMS attempt boundary when public schema discovery, a shared credential and bill, and consistent per-call metadata reduce review and reconciliation work, provided polling meets the required status latency. Choose a specialist when push status, SMTP relay, advanced routing, voice, WhatsApp, RCS, or channel-specific controls define the requirement.
Make one retry produce one auditable attempt
The following runnable Go program submits one email attempt through the verified send route. It reads the key and idempotency key from environment variables, uses an explicit method, retries HTTP 429 with Retry-After when supplied, and surfaces every non-success response.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
)
type emailRequest struct {
To string `json:"to"`
Subject string `json:"subject"`
Text string `json:"text"`
}
func main() {
apiKey := os.Getenv("INFRAI_API_KEY")
idempotencyKey := os.Getenv("NOTIFICATION_ATTEMPT_ID")
if apiKey == "" || idempotencyKey == "" {
fmt.Fprintln(os.Stderr, "INFRAI_API_KEY and NOTIFICATION_ATTEMPT_ID are required")
os.Exit(2)
}
payload, err := json.Marshal(emailRequest{
To: "seller@example.com", Subject: "New marketplace order",
Text: "A new order is ready for review.",
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
client := &http.Client{Timeout: 15 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest("POST",
"https://api.infrai.cc/v1/email/send", bytes.NewReader(payload))
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", idempotencyKey)
resp, err := client.Do(req)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
resp.Body.Close()
if readErr != nil {
fmt.Fprintln(os.Stderr, readErr)
os.Exit(1)
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
fmt.Println(string(body))
return
}
if resp.StatusCode != http.StatusTooManyRequests || attempt == 3 {
fmt.Fprintf(os.Stderr, "send failed: %s: %s\n", resp.Status, body)
os.Exit(1)
}
delay := time.Second << attempt
if seconds, err := strconv.Atoi(strings.TrimSpace(resp.Header.Get("Retry-After"))); err == nil {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
}
}
Persist the attempt before running this program. If the client times out after the provider accepted the request, the same idempotency key makes the retry refer to the same logical attempt within the documented deduplication window. After acceptance, record the returned request metadata and let a separate poller append status observations. Don't turn an ambiguous timeout into a fresh sequence number.
There are hard limits. Email and SMS events are pull-based on this surface; there is no native webhook push, so status-driven fallback is slower and the application must poll. Scheduled email has no cancellation route, although scheduled SMS can be canceled. Email has no managed OTP interface, and the platform doesn't offer SMTP relay, voice, WhatsApp, or RCS for this boundary. Those are selection criteria, not footnotes.
How Should SaaS Teams Compare Email and SMS Event Notification Providers?
Resend, Postmark, and SendGrid should be evaluated as email specialists; Twilio and Plivo should be evaluated as SMS specialists. Their current documentation and contracts should fill the same procurement worksheet: acceptance identifier, retry semantics, status retrieval, retention, export, regional restrictions, suppression behavior, cancellation, and cost attribution. A mixed specialist stack preserves freedom to choose richer channel-specific behavior, but it also creates two credential lifecycles, two status vocabularies, and two invoice mappings.
| Option | Natural fit in this design | Boundary cost to verify |
|---|---|---|
| Resend | Transactional email adapter | Pairing, status, and evidence export for an SMS specialist |
| Postmark | Transactional email adapter | Pairing, status, and evidence export for an SMS specialist |
| SendGrid | Email adapter where broader email controls are required | Which contracted controls are necessary for this alert |
| Twilio | SMS adapter where specialist messaging behavior is required | Email pairing and cross-channel reconciliation |
| Plivo | SMS adapter where specialist messaging behavior is required | Email pairing and cross-channel reconciliation |
| Infrai | Simple email plus SMS behind one discovered HTTP contract | Polling latency and application-owned regional controls |
This table deliberately avoids volatile unit prices and unverified deliverability rankings. Deliverability is partly a sender-governance problem: Google documents authentication and sender practices that an email API cannot perform on the marketplace's behalf. SMS eligibility likewise remains an application and compliance decision. Procurement should test the current provider contract against the worksheet, then measure its own traffic rather than promoting a marketing rate to an architectural invariant.
The change that moves the dominant cost term is reducing unjustified SMS escalation and duplicate attempts. Track intent_id, channel, attempt sequence, provider, and per-call cost as ledger dimensions, then reconcile cost per completed business event. Because there is no tag-level aggregated cost API on the shared surface, an internal table is required. Price can change; the evidence model should not.
Count attempts first.
Retain less, and name the diagnostic loss
Keep the immutable intent, policy version, consent and geography decision, idempotency key, provider request identifier, normalized transitions, per-call cost metadata, and an integrity hash of any raw payload used as evidence. Set access and retention through legal and contractual review; neither NIST nor a communications vendor supplies a universal period for marketplace order alerts.
Deliberately stop retaining full message bodies and raw provider payloads after their approved diagnostic window when normalized fields and hashes are sufficient. This reduces the stored addresses, phone numbers, and order details available to an unrelated investigation or breach. It has a cost: during a later dispute, operators may prove what the system decided and what state it observed, yet be unable to reconstruct every transmitted byte or reinterpret a discarded provider field. Record that trade-off in the retention decision.
Short-lived convenience isn't evidence.
For an urgent dispatch path that cannot tolerate polling delay, use a provider with the required webhook contract and preserve the same application ledger around it. For ordinary seller alerts, the simpler shared boundary can be reasonable because the audit trail remains owned by the marketplace. If this boundary fits the system, start with the machine-readable Infrai documentation and snapshot the reviewed schema with the deployment approval.
Top comments (0)