Outgoing Email And Webhooks
Every email Arky sends and every webhook post, with its type and status
Email and webhook delivery evidence have separate records: OutgoingEmail and
OutgoingWebhook. The business operation retains the outgoing record’s ID.
import type { OutgoingEmail, OutgoingWebhook } from "arky-sdk";
const emails = await sdk.notification.outgoingEmail.find({
store_id: storeId,
to: "customer@example.com",
limit: 20,
});
const email = await sdk.notification.outgoingEmail.get({
store_id: storeId,
id: outgoingEmailId,
});
const posts = await sdk.notification.outgoingWebhook.find({
store_id: storeId,
webhook_id: webhookId,
});
OutgoingEmail has id, type, to, send_before, status, created_at and
updated_at. Its tagged type is arky_sign_in, team_invite, customer_sign_in,
event_email, receipt_resend, template_test, broadcast_email or support_reply.
Event emails retain the event and template IDs; receipt resends retain the Order ID.
The email status is waiting, sending, retrying, sent, not_sent or maybe_sent.
Sending and retrying carry the attempt and recovery evidence; terminal statuses retain
provider acceptance or the factual refusal/uncertainty.
OutgoingWebhook retains store_id, webhook_id, event_id, event_type, status
and timestamps. Its terminal status is acknowledged or unacknowledged.
History
/v1/stores/{store_id}/outgoing-emails sdk.notification.outgoingEmail.find() /v1/stores/{store_id}/outgoing-emails/{id} sdk.notification.outgoingEmail.get() /v1/stores/{store_id}/outgoing-webhooks sdk.notification.outgoingWebhook.find() /v1/stores/{store_id}/outgoing-webhooks/{id} sdk.notification.outgoingWebhook.get() Email filters are to, type, order_id and broadcast_id, with limit and cursor.
Webhook history has a webhook_id filter and pagination.
The unresolved email request is immutable and encrypted. After the email finishes,
its request content is removed; delivery evidence remains. Raw codes and Order tokens
are never returned in this history.
Nothing Is Stopped
There is no stop. Nothing cancels an email or a post once it waits: not a second invite, not removing a team member, not a new code request, not an unsubscribe or a new suppression, not disabling a webhook. Instead, right before each try, Arky checks whether the message may still go, and a message that may not ends with its reason. There is no resend either: “resend receipt” makes a new outgoing email.
How Sending Works
Before each email try Arky checks, in order:
- the deadline: past
send_before, the email endsnot_sentwithsend_before_passed; - the retry window and the tries left: at most 8 tries within 23 hours of the first;
- the store: a store being closed ends it
not_sentwithstore_closing, a store whose Arky plan has no access withstore_unavailable; - the store’s email sending: while it is paused, the email waits without using a try;
- the sending address and the store’s email provider: an archived address, one without a sending
side, or a store without a connected Resend account ends it
not_sentwithsender_cannot_send; - the store’s suppressions: a broadcast stops at any active entry for the address, a sign-in code
at none, every other store email at a block, a hard bounce or a complaint (
recipient_suppressed).
Arky’s own mail skips the store checks. An email refused when it is queued, such as wording with no
text in its language (no_text_in_language) or wording that can’t be built from the email’s data
(render_failed: <reason>), starts not_sent and never reaches Resend.
Sending through Resend:
- Store email goes through the store’s own Resend account and Arky’s own mail through Arky’s. Each account paces its own sends, so one store’s broadcast never slows another store’s mail.
- The idempotency key is the email’s send key: its id, or for a broadcast email the broadcast and a
hash of the address. An unclear answer (no answer, or a
408,409,424,425or5xx) is tried again with the same key after 1 minute, 5 minutes, 30 minutes, 2, 6 and 12 hours. A429puts the email back without using a try. A refusal ends itnot_sent. - After a try whose acceptance is uncertain, the email never ends
not_sent: a Resend report settles itsent, or it endsmaybe_sent. - No duplicates. An event email is unique per event, template and address; a broadcast email per broadcast and address; a webhook post per webhook and event. Handling the same event twice makes no second email.
- Reports. Resend’s signed reports say whether an email was delivered, delayed, bounced or failed, and whether it was reported as spam. A hard bounce or a complaint adds the store’s suppression for that address.
Webhook posts read the webhook’s current URL, headers and secret on each try. A 2xx answer
ends the post acknowledged. Otherwise it is tried again after 1 minute, 5 minutes, 30 minutes, 2,
6, 12 and 24 hours, and the eighth failure ends it unacknowledged. A disabled or deleted webhook,
or a store being closed, ends a waiting post unacknowledged at its next try.
Cleanup. A finished outgoing email or webhook post is deleted 30 days after it finished; a support message keeps its own send history.
Never create a second email because one is maybe_sent. Arky can’t prove
whether Resend accepted the first one.
Reading outgoing emails needs the store’s email permission. Reading webhook posts needs the
store’s admin permission, like managing webhooks.