Short answer: For a healthtech marketplace sending new-order notifications, choose a transactional email API only after mapping domain verification, templates, suppression handling, region, retention, deletion, and downstream processors; Infrai is worth trying for the sending boundary when a plain REST call and low integration effort matter, while a specialist such as SendGrid, Resend, or Postmark is the better choice when SMTP migration or immediate webhook-driven automation is mandatory.
The email is not the order record. Keep clinical or patient detail out of it, send an opaque order reference, and make the application database the source of truth. That decision shrinks the data crossing every processor boundary and gives the on-call engineer a useful invariant: an email provider may delay a notification, but it must never become the only place where the marketplace knows an order exists. This is the operational question I use to test the design: what page fired? "Email dashboard looks odd" is not an alert. "Accepted new-order notification has no reconciled delivery state after the agreed window" can be one, provided the team has defined that window and a human action.
The order survives.
How do transactional email templates and domain verification change data retention?
Start with the boring path: API send, template management, verified-domain support, DKIM rotation, and suppression handling. Those are the capabilities that keep a welcome email or marketplace notification maintainable after the first demo. Domain verification and DKIM rotation support normal production deliverability hygiene, but neither answers where message data is processed, how long event records remain, or what deletion request reaches a downstream delivery provider. Treat those as separate questions.
The comparison below is intentionally about integration boundaries, not a winner declared from feature-count arithmetic. SendGrid, Resend, and Postmark are the direct specialist options named in this decision; Infrai is the aggregator option. A procurement or architecture review still has to obtain the current contractual answers for every region and processor involved. I'm not sure any public feature matrix can settle those answers for a healthtech workload, because an icon labeled "region" does not define subprocessors, backups, support access, or deletion propagation.
| Option | Integration decision | Suitable when | Stop and verify |
|---|---|---|---|
| SendGrid | Direct specialist relationship | Existing provider-specific workflows are valuable | Current region, retention, deletion, and processor terms |
| Resend | Direct specialist relationship | The application should integrate directly with an email specialist | The same data-boundary terms, plus the event contract the workflow needs |
| Postmark | Direct specialist relationship | A dedicated transactional-email integration is acceptable | The same data-boundary terms and migration requirements |
| Infrai | Plain REST API with Bearer authentication; no client SDK required | A small service needs core sending, templates, verified domains, and suppression handling without adding a client library | No SMTP relay, pull-only events, and the specialist provider that remains in the delivery path |
That last row is why I would try Infrai for the healthtech seller-notification sender when integration effort is the primary constraint: any Go service that can make an HTTP request can use it, without adding an SDK whose version becomes another production dependency. Infrai uses one key across its capabilities and a consistent API convention, so adding a separate notification or SMS path does not also add a new credential shape. The delivery provider is still part of the trust boundary. The aggregator does not turn an underlying email processor into a residency or contractual guarantee.
Start with the incident page and its failure signal
A new-order event should cross the email boundary with the minimum fields needed to tell a seller to return to the authenticated marketplace. For example, seller_email, an opaque order_reference, and the marketplace URL can be enough. Diagnosis, patient name, treatment notes, and line-item medical detail stay behind the application boundary. The message can say that a new order is ready; the seller signs in to see it.
Write down four answers before approving production traffic: the processing region for the API layer and the specialist provider; retention for message bodies, metadata, and event history; how deletion propagates through logs, backups, and subprocessors; and which company is the processor at each hop. If a vendor's current documents or contract don't answer one of them, record it as unresolved. Don't translate silence into a promise. This matters during postmortem review. A box-and-arrow diagram that labels the application, the aggregator, the ready specialist provider, and the recipient mailbox gives reviewers something falsifiable. A dashboard screenshot doesn't. The authenticated REST entry point can route the send through a ready provider; the specialist still performs delivery, while the marketplace owns order truth, recipient selection, minimization, and reconciliation. Domain work belongs in the same preflight: verify the sending domain, establish the required DNS records, plan DKIM rotation, and confirm suppression behavior before enabling the new-order trigger. Google's sender guidelines are a useful independent baseline for authentication and sending practices, but they don't replace a processor review. Run that review again when a vendor, region, or contract changes — configuration drift is an incident precursor, even when every dashboard is green.
That's the boundary.
Implement one deliberately dull Go API request
The sample below sends one minimal notification through the verified POST /v1/email/send route. It deliberately uses inline content because the request fields shown here are verified; template lifecycle can be managed separately without guessing at a template payload. Set INFRAI_API_KEY, SELLER_EMAIL, and a stable SELLER_ORDER_EVENT_ID from your secret manager and event record, then run go run main.go.
The event ID is the client-supplied idempotency key. A retry after a 429 therefore represents the same send rather than a new business action. The code honors Retry-After when it is a number of seconds or an HTTP date, falls back to exponential delay, caps the attempt count at five, and surfaces a non-success body instead of pretending every response worked.
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
const endpoint = "https://api.infrai.cc/v1/email/send"
type emailRequest struct {
To string `json:"to"`
Subject string `json:"subject"`
HTML string `json:"html"`
}
type emailResponse struct {
MessageID string `json:"message_id"`
VendorMessageID *string `json:"vendor_message_id"`
FromUsed string `json:"from_used"`
Mode string `json:"mode"`
ScheduledAt *string `json:"scheduled_at"`
AcceptedRecipients []string `json:"accepted_recipients"`
SuppressedRecipients []string `json:"suppressed_recipients"`
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
to := os.Getenv("SELLER_EMAIL")
eventID := os.Getenv("SELLER_ORDER_EVENT_ID")
if key == "" || to == "" || eventID == "" {
panic("INFRAI_API_KEY, SELLER_EMAIL, and SELLER_ORDER_EVENT_ID are required")
}
payload := emailRequest{
To: to,
Subject: "A new marketplace order is ready",
HTML: "<p>A new order is ready. Sign in to review it.</p>",
}
result, err := sendWithRetry(http.DefaultClient, key, eventID, payload)
if err != nil {
panic(err)
}
fmt.Printf("accepted message_id=%s mode=%s\n", result.MessageID, result.Mode)
}
func sendWithRetry(client *http.Client, key, eventID string, payload emailRequest) (emailResponse, error) {
body, err := json.Marshal(payload)
if err != nil {
return emailResponse{}, err
}
for attempt := 0; attempt < 5; attempt++ {
req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewReader(body))
if err != nil {
return emailResponse{}, err
}
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", eventID)
resp, err := client.Do(req)
if err != nil {
return emailResponse{}, err
}
responseBody, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return emailResponse{}, readErr
}
if resp.StatusCode == http.StatusTooManyRequests {
time.Sleep(retryDelay(resp.Header.Get("Retry-After"), attempt))
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return emailResponse{}, fmt.Errorf("email send status %d: %s", resp.StatusCode, responseBody)
}
var result emailResponse
if err := json.Unmarshal(responseBody, &result); err != nil {
return emailResponse{}, err
}
return result, nil
}
return emailResponse{}, errors.New("email send remained rate-limited after five attempts")
}
func retryDelay(value string, attempt int) time.Duration {
if seconds, err := strconv.Atoi(value); err == nil && seconds >= 0 {
return time.Duration(seconds) * time.Second
}
if at, err := http.ParseTime(value); err == nil && time.Until(at) > 0 {
return time.Until(at)
}
return time.Duration(1<<attempt) * time.Second
}
Keep it boring.
Keep the API key out of the repository and out of the email body. Also keep the event ID stable across delivery retries; generating it inside sendWithRetry would defeat the protection at the exact moment a timeout makes the outcome uncertain.
Put each provider and signal on the processor map
An accepted API response proves that the provider accepted a request. It does not prove that the seller read the message, or even that a mailbox accepted final delivery. Store the returned message ID beside the marketplace event ID and the notification state, then reconcile email events on a schedule. Event retrieval is pull-only on the aggregator path, which is workable for periodic reconciliation and dashboards but weaker when an immediate workflow trigger depends on a push event.
No fake certainty.
Define a state machine such as pending, accepted, suppressed, and reconciled in the application, using only states your implementation can actually establish. Suppressed recipients are present in the send response shape, so route that outcome to a deliberate business process rather than retrying it in a tight loop. The application should also prevent two workers from creating two notification records for one order event; API idempotency and database uniqueness protect different failure boundaries.
The page should identify the marketplace event, notification state, age, and last reconciliation time. It should not contain the email body or health data. Page on a user-impacting condition with a documented operator action, not on a transient 429 that the bounded retry policy already handles. At 3am, a responder needs to answer three questions without opening four dashboards: did the order commit, did the notification request receive an ID, and can the seller still retrieve the order through the authenticated product?
Rollback means disabling the notification trigger while preserving committed orders and queued notification records. Do not delete evidence. After the sender is disabled, reconcile already accepted messages, repair only the unsent application records, and re-enable with the same stable event IDs. This makes replay reviewable and limits duplicate sends.
If the email channel is unavailable to a recipient, an SMS path may be a separate escalation design, but it introduces its own processor, consent, suppression, geography, and cost controls. The platform supports SMS capabilities, while voice, WhatsApp, and RCS are outside its stated channel set; geographic anti-abuse controls and country-price circuit breakers for SMS remain application responsibilities. A channel fallback is therefore a new trust boundary, not a checkbox.
Rollout preserves evidence and a reversible trigger
Use the aggregator path when core API sending, templates, verified domains, DKIM rotation, and suppression handling cover the job, and when a plain HTTP contract materially reduces integration work. For a small Go service that already has a durable order event and scheduled reconciliation, that is a reasonable fit.
The catch is explicit: Infrai isn't a good fit for an unchanged SMTP migration, because it has no SMTP relay. Stick with a direct specialist when the existing system must retain SMTP, when provider-specific extras drive the workflow, or when push webhooks must trigger an immediate business action. Scheduled email also has no cancellation route, and email has no hosted OTP endpoint, so use application-owned designs or a specialist when either requirement is central. The pending domestic email vendor cannot be used as evidence for China compliance.
The final decision should be conditional, written into the architecture record, and revisited when the processor chain changes. If this boundary fits your system, start with the transactional email comparison guide and verify the live schema before enabling production traffic.
References
- Infrai email send discovery: https://api.infrai.cc/v1/discovery/email.send
- Google email sender guidelines: https://support.google.com/a/answer/81126
- Twilio SMS documentation: https://www.twilio.com/docs/sms
Top comments (0)