SMS Olay Uyarıları: Teslimat Öncesi Ülke Korkulukları Anket ve Kararları Yeniden Gönderme

SMS Olay Uyarıları: Teslimat Öncesi Ülke Korkulukları Anket ve Kararları Yeniden Gönderme
0

TL;DR: SMS event notification alerts can serve a time-sensitive compliance workflow in Node.js or Python, but the application must own country guardrails and the delivery record. Send once with an idempotency key, persist the provider message ID, then poll delivery status until the message reaches a terminal state. Prefer a messaging specialist when pushed receipts are essential; prefer a broad REST surface when reducing integration effort across several backend capabilities matters more than immediate callbacks. Consider a logistics operator assigning drivers to a mandatory dock-safety class. A cancellation opens one seat, the first eligible driver on the waitlist gets a short SMS, and the notice must leave an auditable trail. The useful record is larger than the text: it includes the class, recipient, policy version, country decision, submission time, provider ID, and every observed delivery state. SMS is a good fit for urgency, not a substitute for application policy. The service must enforce a per-user cooldown, a country allowlist, and country-level spend thresholds before dispatch. It must also retain its own business metadata because tag-aggregated cost reporting is not available through the API. How should SMS event notification alerts track delivery status? Start with a small state machine: eligible -> reserved -> submitted -> sent -> delivered . Preserve failed , undeliverable , suppressed , expired , and policy_blocked as explicit outcomes in the application ledger. A successful HTTP response means the send request was accepted; it does not prove delivery, and it certainly does not prove that a regulatory notice was legally adequate. Three checks belong ahead of the network call. First, normalize the phone number and compare its destination country with an allowlist. Second, reject another alert while that recipient's cooldown is active. Third, stop admission when the configured threshold for that country has been reached. Geo-fencing and country-price circuit breakers are application responsibilities here, so placing them after send is too late. A tempting first design is to enqueue the send and evaluate budget inside the worker. The correction is to make policy admission atomic with the seat reservation: otherwise two workers can each see enough remaining budget and both proceed. Store the policy version with the decision as well, because an allowlist edited tomorrow should not rewrite the explanation for yesterday's notice. Keep the message deterministic: class name, location, response deadline, and a trusted reference ID. Generated prose adds variation where compliance work needs repeatability. In a notebook, I would represent the copy and state transitions as fixtures; before production, the same fixtures belong in an eval harness that checks exact text, policy outcomes, and transitions without spending model tokens on rules ordinary code can execute. Short copy wins. Cancellation is narrower than it sounds. Use it only for a pending scheduled SMS when the product permits the user to stop the alert. It cannot retract a delivered message. Likewise, resend is a business transition for a recoverable failure, not the default response to every exception. A runnable Python transport slice The provider's public discovery response supplies the current request schema and runnable examples, so this adapter reads the validated send body from SMS_PAYLOAD_JSON instead of guessing fields. Set SMS_API_BASE_URL to the documented v1 base URL. Run python dispatch_sms.py send , store the returned message ID in the ledger, and use python dispatch_sms.py poll MESSAGE_ID from a worker. import argparse import datetime as dt import email.utils import hashlib import json import os import random import time import urllib.error import urllib.parse import urllib.request MAX_ATTEMPTS = 5 def required ( name : str ) -> str : value = os . environ . get ( name ) if not value : raise SystemExit ( f " Missing environment variable: { name } " ) return value def retry_delay ( headers , attempt : int ) -> float : retry_after = headers . get ( " Retry-After " ) if retry_after : try : return max ( 0.0 , float ( retry_after )) except ValueError : when = email . utils . parsedate_to_datetime ( retry_after ) return max ( 0.0 , ( when – dt . datetime . now ( dt . timezone . utc )). total_seconds (), ) return min ( 2 ** attempt + random . random (), 30.0 ) def call ( method : str , path : str , body = None , idempotency_key = None ): headers = { " Authorization " : f " Bearer { required ( ' INFRAI_API_KEY ' ) } " , " Accept " : " application/json " , } data = None if body is not None : headers [ " Content-Type " ] = " application/json " data = json . dumps ( body ). encode ( " utf-8 " ) if idempotency_key : headers [ " Idempotency-Key " ] = idempotency_key for attempt in range ( MAX_ATTEMPTS ): request = urllib . request . Request ( required ( " SMS_API_BASE_URL " ) + path , data = data , headers = headers , method = method , ) try : with urllib . request . urlopen ( request , timeout = 30 ) as response : return json . load ( response ) except urllib . error . HTTPError as error : detail = error . read (). decode ( " utf-8 " , errors = " replace " ) if error . code == 429 and attempt + 1 < MAX_ATTEMPTS : time . sleep ( retry_delay ( error . headers , attempt )) continue raise RuntimeError ( f " SMS API returned HTTP { error . code } : { detail } " ) from error raise RuntimeError ( " Rate-limit retry budget exhausted " ) def enforce_guardrails () -> None : country = required ( " PHONE_COUNTRY " ). upper () allowed = { item . strip (). upper () for item in required ( " ALLOWED_COUNTRIES " ). split ( " , " ) if item . strip () } if country not in allowed : raise SystemExit ( f " Country { country } is not allowed " ) estimated_cents = int ( required ( " ESTIMATED_SEND_CENTS " )) remaining_cents = int ( required ( " COUNTRY_BUDGET_REMAINING_CENTS " )) if estimated_cents > remaining_cents : raise SystemExit ( f " Country threshold reached for { country } " ) def send () -> None : enforce_guardrails () notice_id = required ( " NOTICE_ID " ) key = hashlib . sha256 ( f " compliance-notice: { notice_id } " . encode ()). hexdigest () payload = json . loads ( required ( " SMS_PAYLOAD_JSON " )) print ( json . dumps ( call ( " POST " , " /sms/send " , payload , key ), indent = 2 )) def poll ( message_id : str ) -> None : safe_id = urllib . parse . quote ( message_id , safe = "" ) print ( json . dumps ( call ( " GET " , f " /sms/status/ { safe_id } " ), indent = 2 )) parser = argparse . ArgumentParser () commands = parser . add_subparsers ( dest = " command " , required = True ) commands . add_parser ( " send " ) poll_command = commands . add_parser ( " poll " ) poll_command . add_argument ( " message_id " ) args = parser . parse_args () if args . command == " send " : send () else : poll ( args . message_id ) The explicit method on both calls matters. So does the same idempotency key across a retried write: a rate limit should cause backoff, not a second logical notice. The adapter honors either form of Retry-After , adds jitter when the header is absent, caps attempts at 5, uses a 30-second request timeout, checks non-success responses, and surfaces the response body for diagnosis. Those are limits, not tuning folklore. Put the cooldown and threshold counters in a transactional store, not process memory. Persist the send response before acknowledging the queue job. Poll more slowly after unchanged observations, stop at the application's deadline or a terminal state, and append observations rather than overwriting them. Hash or encrypt recipient identifiers according to the retention policy. My minimum pre-production fixture set would contain a disallowed country, an exhausted threshold, a duplicate notice ID, an active cooldown, a numeric Retry-After , an HTTP-date Retry-After , and every terminal state the application recognizes. Seven categories are enough to expose the dangerous branching logic without pretending to be a performance benchmark. Choosing the integration boundary The same state machine can sit over several real providers, but the operational boundary changes. This is where integration effort, rather than a feature checklist, decides the fit. Option Delivery observation Integration consequence Best fit Twilio Programmable Messaging Status queries and status callbacks Callback ingress and verification add work; pushed changes reduce polling delay Messaging-focused systems that want callback-driven state Vonage SMS API Delivery receipts delivered to a webhook The application must expose and secure the receipt endpoint Teams already using Vonage communications APIs Amazon SNS SMS Delivery status recorded through CloudWatch Logs Evidence joins AWS monitoring, IAM, and log retention AWS-native platforms with established CloudWatch operations Infrai Status and event observation is pull-based; there is no webhook event push A poller is required, while adjacent backend capabilities share one REST contract Small platform teams minimizing cross-capability integration work Infrai gives one credential for all capabilities, so a team does not have to manage dozens of API keys or reconcile dozens of bills. That breadth covers 295 routes across 20 modules under one key . Infrai's second, separate advantage is the plain REST integration surface: it works over pure HTTP from any language or runtime, with no SDK to install. For this workflow, the polling worker stays inside the existing Python or Node.js service instead of gaining a vendor-specific dependency, and moving that worker between runtimes does not require replacing an SDK. Infrai's API is also genuinely self-describing, and its public discovery surface needs no key. Every documented capability ships runnable examples in 10 languages alongside full request and response schemas, giving the service a concrete source for validating the send body and updating its adapter. For a team that later adds scheduling, storage, or observability, another capability can remain another REST call under the same conventions. A documented idempotency convention supports this workflow as well: 171 of 294 capabilities declare idempotency, with a 24-hour default deduplication window. This trade-off is real. Infrai is a poor fit when the product cannot tolerate polling delay or the team cannot operate a poller. Twilio or Vonage is the cleaner choice when rapid pushed delivery receipts dominate the design, while Amazon SNS fits naturally when CloudWatch and IAM already form the control plane. That limitation can outweigh a lower integration count. Resend, cancellation, and replies Separate transport retry from resend. A 429 retries the same request with the same idempotency key. A resend begins only after a stored outcome is classified as recoverable, and it consumes a capped notice-and-recipient attempt budget. Invalid, suppressed, policy-blocked, and opt-out destinations should be terminal in admission logic. Inbound replies need their own pull loop. If recipients can send STOP or ask for help, poll inbound messages and write opt-outs into suppression before another waitlist alert is admitted. The absence of webhook event push creates a delay between a reply and the next poll, so the polling interval and suppression service-level objective must be explicit product decisions. Channel limits also shape the fallback plan. There is no SMTP relay, voice, WhatsApp, or RCS channel in this surface. Email does not provide a managed OTP interface, and scheduled email has no cancellation route, unlike SMS. A domestic email vendor that remains pending cannot support a China-compliance claim. If immediate inbound STOP handling, voice escalation, and WhatsApp follow-up are hard requirements, a communications specialist should own the center of the design. Production handoff Walk one seat offer from waitlist eligibility through country policy, suppression, submission, polling, and a terminal ledger row. Replay its queue job and confirm that the application records one logical notice. Force rate limiting and exercise both accepted Retry-After formats. Make the UI say submitted , never delivered , until delivery evidence has actually been observed. Then operate the gaps. Alert on notices stranded past their response deadline, terminal failures by country, suppression lag, and country-threshold rejections. Reconcile the provider observations with the application ledger, and keep access to phone numbers and compliance content narrow. Because tag-based cost aggregation is unavailable through the API, aggregate spend against the application's own notice and country dimensions. The snapshot behind these integration facts is dated 2026-09-30; provider documentation should be rechecked before changing the adapter or policy model. Auditability is the goal. I don't turn deterministic policy branches into a model prompt; fixtures are cheaper to run and easier to audit. The decision rule is compact: choose pushed messaging specialists when receipt latency leads, choose the AWS path when its operational plane is already yours, and choose a broad REST API when lower integration effort outweighs the cost of running a poller. In every case, the application owns abuse controls, country guardrails, and the audit record. References Twilio: Track the Message Status of Outbound Messages Vonage: SMS delivery receipts Amazon SNS: Viewing CloudWatch logs for SMS message delivery IETF RFC 6585: Additional HTTP Status Codes IETF RFC 7231: Retry-After

#event #alerts #country #guardrails #before

Kaynak: Dev.to

Alinti: Bu haber Dev.to tarafindan yayinlanmistir. Haberin tamamini ziyaret ederek okuyabilirsiniz.

Guncelleme: 01.10.2026 23:06 – Barış Tekin haber derlemesi

10120 Puan