Skip to main content

What an app can do

An installed app can manage DNS in two ways, depending on where the domain was bought:
  • Domains registered by Salesive (provider: "dynadot"): read the zone and add, change or remove individual records.
  • Custom domains bought elsewhere (provider: null): file a record request that the merchant applies at their own registrar. Salesive verifies it and notifies your app.
Every write is one record at a time. Apps never get the full zone replace, nameserver changes, transfer lock, renewal or domain deletion. Those stay with the merchant and the platform.
WRITE_DOMAINS is a different runtime permission. It covers adding and removing domains and changing the store subdomain. It does not allow DNS writes. The full zone replace (PUT /domains/:id/dns) is refused for app tokens even with every permission granted.

Request the WRITE_DNS permission

WRITE_DNS is a runtime permission, not an OAuth scope. Your app asks for it from inside its iframe, the merchant sees a dashboard modal, and the grant is stored on the installation for a duration the merchant chooses: once, 1day, 7days, 14days or 31days. A WRITE_DNS grant is always scoped to one domain, so a bug or a bad actor cannot touch another zone in the store. You can also limit it to specific record types, and give the merchant a reason for the request.

Through the App Bridge

The second argument can also stay a plain duration array, so existing requestPermission calls are unchanged.

Raw postMessage

If you do not use the helper package, send the same fields to the dashboard:
The dashboard answers with a salesive:permission-response message carrying permission, granted, mode and expiresAt.
Grants die with the installation. Reinstalling the app starts with no DNS access until the merchant approves a new grant. The merchant can revoke a grant at any time from the dashboard.

Endpoints

All routes are under https://api.salesive.com/api/v1/domains. Your app token is bound to one store, so you never send a shop id.

List records

GET /domains/:id/dns/records is the read to use before a write. Each record carries:
  • id: the deterministic record id, used in PATCH and DELETE.
  • owner: app, merchant or platform.
  • installationId: the installation that owns an app-created record, otherwise null.
  • protected: true when the record cannot be changed by an app.
GET /domains/:id/dns returns the same zone in the dashboard’s older shape ({ domain, ttl, records: [{ id: "main-0", host, type, value, value2 }] }). Its ids are positional, not stable, so use /dns/records when you intend to write.

Add a record

Use @ for the root record, or the subdomain label only (send, not send.acme.com). MX records carry their priority in value2 as a numeric string:
The record endpoints do not change the zone TTL. The zone keeps its existing TTL, which is between 60 and 86400 seconds.

Change a record

PATCH /domains/:id/dns/records/:recordId replaces the whole record, so send host, type, value and value2 every time. A PATCH is not a partial update.
The id is derived from the record’s content, so it changes when the record changes. Use the id in the response for any follow-up write, or list the zone again.

Remove a record

How recordId is built

Dynadot has no record ids, so Salesive derives one:
host and type are lowercased and values are trimmed before hashing. The same record always has the same id, so your app can address a record straight from a list response without a second lookup. Because content is part of the hash, an edit produces a new id.

Protected records and ownership

Apps own only the records they created. Ownership is stored next to the zone, keyed by the deterministic record id, and it is checked before every write. The root A and AAAA records point the domain at the storefront. A record carrying the storefront address is what makes the store resolve, so no app may set or remove it. Nameservers, transfer lock, renewal and domain deletion are never reachable with an app token.
protected: true on a list response is advisory: it tells your UI which records cannot be edited, and the same checks run again on the write. A record can be protected because of its type or value even when no app owns it.

Limits

Writes to a domain are serialised so two requests cannot read, change and write the same zone at once. If another write holds the domain lock, the request fails with 409 DNS_BUSY and can be retried after a moment.

Error codes

DNS errors return the message and a top level code:
A missing runtime grant answers with the code nested under data:
A missing OAuth scope answers with 403 and a message naming the missing scope.

Custom domains bought elsewhere

Salesive cannot write DNS at a registrar it does not control. For a custom domain connected to the store, your app files a request instead. The merchant copies the values into their own registrar, and a recurring job resolves the record until it matches.
1

Find the domain id

Call GET /domains with READ_DOMAINS and pick the custom domain. The id is the _id of the domain, not the domain name.
2

Request WRITE_DNS for that domain

Send the App Bridge request with scope.domainId and, if you know them, the record types you will need.
3

File the record request

POST /domains/:id/dns/requests with the host, type, value, optional TTL and a short purpose.
4

Merchant applies it

The domain page lists pending requests with copy and paste values and a check now button. The merchant adds the record at their registrar.
5

Salesive verifies it

The verifier runs every five minutes, resolves the record, and marks the request verified. Your app receives a domains/dns_verified webhook.
The same protection rules apply to requests: root A and AAAA, the storefront address, NS and SOA are refused. Filing the same pending request twice returns the existing request instead of creating a duplicate. GET /domains/:id/dns/requests lists requests for the domain. An app token sees only the requests filed by its own installation. The verifier checks A, AAAA, CNAME, MX, TXT, NS and SRV records and gives up after 120 checks, which is about ten hours at the five minute cadence. A request that never matches is marked rejected, with the reason in lastError.

Webhooks

DNS changes send their own topics instead of the generic domains/updated, so your app can tell a record write apart from a domain being added or removed. Every delivery needs READ_DOMAINS, and like all Salesive webhooks it carries identifiers only. Re-fetch the zone with GET /domains/:id/dns/records. domains/dns_verified adds the request id and the record that was verified:

Audit trail

Every DNS write is recorded, whether it came from an app or the merchant. The entry stores the installation, the actor, the action, the record id, and the before and after snapshots. GET /domains/:id/dns/changes returns entries newest first. limit defaults to 50 and is capped at 200. Reverting is a merchant action (POST /domains/:id/dns/changes/:changeId/revert), because it can restore a record the app does not own.

Next steps

Scopes & permissions

The OAuth scopes and runtime permissions an app can request.

App Bridge

Request runtime permissions and handle lifecycle events.

Webhooks

Signature verification, retries and the full topic list.

List domains

Find the domain id before requesting a grant.