Store Email
Connect the store's Resend account, add a sending address, and pick the email of each moment.
Arky sends a store’s email itself, signed with the store’s own domain, the way Shopify, Klaviyo and Ghost do. A store sends through its own Resend account, as it takes card payments through its own Stripe account: Arky decides who gets what and when, and Resend only sends. There is no shared Arky address: each store carries its own sending reputation, and Arky never sends a store’s mail under its own name.
A new store sends no email until it connects its Resend account, adds a sending address and picks the template of each moment. That includes order receipts and the sign-in codes storefront customers use, so make this part of every launch.
1. Connect The Store’s Resend Account
Add and verify the store’s domain in its own Resend account, then connect the account with a full-access API key:
import { createAdmin } from "arky-sdk/admin";
const sdk = createAdmin({ apiUrl: "https://api.arky.io" });
await sdk.notification.emailProvider.connect({ store_id: storeId, id: crypto.randomUUID(), api_key: resendApiKey });
const { items: domains } = await sdk.notification.emailProvider.findDomains({ store_id: storeId });
const domain = domains.find((value) => value.name === "northwind.example");
if (domain?.status !== "verified") console.log("Finish the domain's DNS records in Resend first");
- Connecting checks the key and registers a report webhook in the store’s Resend account, so delivery, bounce and complaint reports come back to Arky.
- The domain list is read live from Resend; Arky keeps no copy. Resend checks the domain when it
receives each email: an email from a domain it hasn’t verified ends
not_sentwith Resend’s reason. replaceKeyswaps the key, with the version you read. The provider keeps its id, and mail already queued under the old key’s account endsnot_sent.
2. Add A Sending Address
const orders = await sdk.notification.emailAddress.create({
store_id: storeId,
id: crypto.randomUUID(),
email: "orders@northwind.example",
type: { type: "sending", sending: { from_name: "Northwind", reply_to: "orders@northwind.example" } },
});
Customers see Northwind <orders@northwind.example>, and their replies go to the reply_to
address, the store’s own inbox.
A store can have more addresses, such as news@ for broadcasts and support@ for support, and an
address can also receive (see Support). The email settings and each broadcast
name their sending address, and nothing ever switches address on its own: if an address can’t send,
its emails end not_sent and staff see why.
3. Pick The Email Of Each Moment
A template is the wording of one email, with a fixed type
and its text per language. Creating one selects nothing: the store’s email settings (StoreEmails)
name the sending address and the template each moment sends.
sdk.notification.template.findDefaults({ store_id }) lists Arky’s default wording to copy into a
new template.
const receipt = await sdk.notification.template.create({
store_id: storeId,
id: crypto.randomUUID(),
key: "order-receipt",
type: { type: "order" },
content: {
en: {
subject: "Thanks for your order {{order.number}}",
preheader: null,
body: '<p>{{media "6f4c2f0e-1d2b-4c1a-9a51-6a2a3d0f9b10"}}</p><p>We got your order {{order.number}}.</p>',
},
},
});
const { id: _id, store_id: _store, created_at: _created, updated_at, ...current } = await sdk.store.emails.get({
store_id: storeId,
});
await sdk.store.emails.update({
...current,
store_id: storeId,
expected_updated_at: updated_at,
sending_address_id: orders.id,
order_received: { email_template_id: receipt.id },
});
| Moment | Selection |
|---|---|
| a customer asks for a sign-in code | sign_in_code |
| an order is placed, including pending payment | order_received |
| a contact’s first order is confirmed | first_order_placed |
| a shipment leaves, arrives, or a pickup is ready | order_dispatched, order_delivered, order_ready_for_pickup |
| an order is cancelled or refunded | order_cancelled, order_refunded |
| a booking is confirmed or moved, before it, or cancelled | booking_confirmed, booking_reminder, booking_cancelled |
| an offer is sent or revised, or still waits | offer_sent, offer_reminder |
| a customer sends a form, or it reaches a stage | FormEmails.received, FormEmails.stage_changed |
| someone is added to a company | company_invite |
| a return is requested, approved or declined | return_requested, return_approved, return_declined |
| a customer group renewal comes up or its payment fails | renewal_upcoming, renewal_payment_failed |
| a recurring membership is cancelled | membership_cancelled |
| a customer proves a new email address | email_changed, to the old address |
| the team should hear about an order, return or form | new_order_alert, new_return_alert, FormEmails.alert |
- No moment can be selected while the settings have no sending address
(
409 STORE_EMAILS.SENDER_REQUIRED); once set, the address can be replaced but never cleared, so a selected email always has a sender. - Each email goes in the language of what it’s about: an order’s emails in the order’s language,
a form’s in the submission’s, the sign-in code in the language its request names. A template
without text in that language sends nothing for it: the email ends
not_sent. - An unselected moment sends nothing. To stop one, set its selection to
null. - Staff choose whether the customer gets an email each time they cancel, move a booking, refund, mark a shipment, or decide a return.
- Preview and test send fill the body with a sample of exactly the data the email will get.
- Some fields come with only one moment of a template family, such as the receipt’s
order_token. Wording that shows such a field outside an{{#if}}on it can’t be selected for a moment that doesn’t carry it (409 EMAIL_TEMPLATE.FIELD_NOT_SENT). A selected email whose wording still can’t be built from its data is recordednot_sentwithrender_failed: <reason>, and the action that sent it goes on; a code request whosesign_in_codewording can’t be built is refused instead.
Storefront Sign-In Codes
Storefronts never send email themselves. A customer sign-in code goes out with the template the store
selected for sign_in_code; the storefront only asks for it, in the language it names:
import { initialize } from "arky-sdk/storefront";
import { createOrderToken } from "arky-sdk/utils";
const arky = initialize("arky_pk_...", {
locale: "en",
createVisitorIdentity: () => ({
customer_id: crypto.randomUUID(),
session_id: crypto.randomUUID(),
}),
createSessionId: () => crypto.randomUUID(),
createId: () => crypto.randomUUID(),
createOrderToken,
});
await arky.customer.requestCode({
email: "ana@example.com",
language: "en",
});
await arky.customer.verify({ code: "123456" });
- The code email’s id comes from your
createId. A retry after a lost answer reuses it, so one email goes out; a request after an answer, such as “send me another code”, gets a new one. Passidto pick it yourself.verifytakes the id of the signed-in session the code makes fromcreateSessionId, and keeps it when it retries that verification. - Code requests are refused at once while the store has no
sign_in_codeemail selected, its template has no text in the language, the sending address isn’t ready or the store’s email is paused, so show your own message then. With a bot check on the publishable key, send the widget’s token asbot_check_token. - The same email sends the code that proves a new address when a signed-in customer changes their email.
What Arky Handles
- Bounces and spam complaints come from the store’s Resend reports and block the address for that store. Unsubscribes from broadcast emails stop broadcasts to the address.
- Nothing is stopped. An email that waits is never cancelled. Right before each try Arky checks
the deadline, the store, its email provider, the sending address and the store’s suppressions,
and an email that may no longer go ends
not_sentwith its reason. - Pausing. The store’s owner, or a platform administrator, can pause all of a store’s email, such as to catch a mistaken broadcast: emails wait until sending is allowed again. See Pause Store Email.
- History. Every email is an outgoing email: staff search by address and see each email’s type and status. A finished one is kept 30 days.
- Arky’s own mail, the staff sign-in code, the team invite and store billing mail, goes through Arky’s own Resend account from Arky’s address.
Support email works the other way round: the store forwards its support address to an Arky receiving address, and the team replies from Admin through a sending address. See Support. For newsletters, see Newsletters and Broadcasts.