Timezones
GoSendAPI Cloud is built for multi-country SaaS. Every timestamp in our API is UTC. This page covers what that means, how it affects your integration, and the patterns we recommend for displaying times to end users in different timezones.
TL;DR: We store and return UTC (ISO 8601 with Z suffix). Convert to your user’s local timezone at render time on the client.
Why UTC everywhere
Timezone bugs are silent. They don’t crash your app — they just send the wrong message at 3am, or show the wrong date on a report, or trigger a 24-hour billing window to close 3 hours early. UTC eliminates an entire class of these bugs by removing ambiguity.
When you operate across countries, “8 PM” without a timezone is meaningless:
- 8 PM in Buenos Aires (UTC-3) is 11 PM UTC
- 8 PM in Mexico City (UTC-6) is 2 AM UTC the next day
- 8 PM in São Paulo (UTC-3) might be UTC-2 during their summer time
Storing UTC means there’s exactly one correct time per event. Display logic handles the conversion.
What you’ll see in the API
All createdAt, updatedAt, lastActivityAt, expiresAt, occurredAt, and similar fields are ISO 8601 strings with Z suffix:
{
"id": "msg_01H2X...",
"createdAt": "2026-06-06T11:50:52.735Z",
"deliveredAt": "2026-06-06T11:50:54.120Z"
}The same applies to webhook payloads:
{
"event": "whatsapp.message.delivered",
"delivery_id": "684093f1-1d5d-49b9-a788-024e2fddb556",
"occurred_at": "2026-06-06T11:50:52.735Z",
...
}Parsing these in JavaScript automatically respects UTC:
const date = new Date("2026-06-06T11:50:52.735Z");
date.toLocaleString();
// In São Paulo: "06/06/2026, 08:50:52"
// In Madrid: "06/06/2026, 13:50:52"
// In Tokyo: "2026/6/6 20:50:52"What we expect from you
When you send a timestamp to our API (rare — most fields are server-managed, but scheduleAt exists on some flows), use ISO 8601 with explicit timezone:
// ✅ Good — explicit UTC
{ "scheduleAt": "2026-06-10T18:00:00Z" }
// ✅ Good — explicit offset
{ "scheduleAt": "2026-06-10T15:00:00-03:00" }
// ❌ Bad — ambiguous, may be rejected
{ "scheduleAt": "2026-06-10 15:00:00" }We convert to UTC internally before storing.
Rendering to your end users
The most common mistake: showing UTC to a user without converting. The fix is straightforward — use the user’s browser timezone, or a timezone you have explicitly stored for them.
Pattern 1: client-side Intl (recommended for portals)
const utcIso = "2026-06-06T11:50:52.735Z";
const date = new Date(utcIso);
new Intl.DateTimeFormat(navigator.language, {
dateStyle: "medium",
timeStyle: "short",
}).format(date);
// User in Buenos Aires sees: "6 jun 2026, 08:50"
// User in Tokyo sees: "2026/06/06 20:50"Intl.DateTimeFormat uses the browser’s timezone automatically when you don’t pass timeZone. This is what you want 95% of the time.
Pattern 2: explicit timeZone (when you know the user’s TZ)
new Intl.DateTimeFormat("es-AR", {
timeZone: "America/Argentina/Buenos_Aires",
dateStyle: "medium",
timeStyle: "short",
}).format(date);Use this when:
- You operate a backend that emails / SMS users in their own timezone
- You have stored the user’s preferred timezone in your DB
- You’re generating a report that must be in a fixed regional context
Pattern 3: server-side rendering — avoid
If your portal renders timestamps server-side using Date.toLocaleString(), you’ll get the container’s timezone, not the user’s. Most container hosts run UTC, so users see UTC everywhere — exactly the problem we’re trying to solve.
If you must SSR a timestamp, render the raw ISO string and let a client component reformat it after hydration.
Common pitfalls
”Send template at 9 AM customer’s time”
Don’t try to express “9 AM in customer TZ” using a UTC timestamp computed at scheduling time — you’ll be wrong every time daylight saving changes.
Instead, store the wall-clock time + the timezone separately:
{
"wallTime": "09:00:00",
"timezone": "America/Argentina/Buenos_Aires",
"weekday": "MONDAY"
}Resolve to a UTC instant only at the moment of sending.
Birthdays, holidays, “fechas legales”
A birthday is a date, not an instant. Don’t store it as DateTime — store it as date (no time component). Storing it as 1990-06-10T00:00:00Z will display as 1990-06-09 for anyone west of UTC.
24-hour service window
WhatsApp’s free service-message window resets at 24 hours after the user’s last inbound message. We track this in UTC and apply the same logic regardless of the customer’s timezone — what matters is the elapsed UTC interval, not the wall clock.
Reference: TZ identifiers
Always use IANA timezone identifiers (America/Argentina/Buenos_Aires), never abbreviations like ART or offsets like UTC-3. Abbreviations are ambiguous (IST is both India and Israel) and offsets break when summer time kicks in.
A few common ones for LATAM:
| Country | IANA identifier |
|---|---|
| Argentina | America/Argentina/Buenos_Aires |
| Brazil (São Paulo) | America/Sao_Paulo |
| Mexico (CDMX) | America/Mexico_City |
| Chile | America/Santiago |
| Colombia | America/Bogota |
| Peru | America/Lima |
| United States (NY) | America/New_York |
| Spain | Europe/Madrid |
A full list lives in the IANA tz database.
Server clock and observability
Our servers run UTC. Our logs are UTC. Our metrics are UTC. If you’re correlating a webhook delivery with one of our delivery_ids, the occurred_at you see in the payload is the same instant we logged on our side — no conversion needed.
If your own server runs a non-UTC timezone, you may want to log incoming webhook timestamps in UTC explicitly to make cross-system debugging easier.