The Storefront API lets you manage your customers' domain portfolio programmatically without needing to log in to Storefront Manager. This is useful if you want to automate renewals, keep domain data in sync with your own platform, migrate a portfolio in bulk, or build tooling for your support team.
Before you begin
Requirements
- An active OpenSRS Storefront with API access enabled
- API credentials (Client ID and Client Secret) — see API DNS Management Guide for how to generate these and obtain an access token
- Your OpenSRS account balance above $10 to keep your Storefront active
Domain endpoints
Get domain details
Returns everything known about one domain: its status and dates, current settings, the customer who owns it, and the transfer auth code.
GET /v1/domain/{domain}
Response: 200 OK — a domain object.
| Field | Type | Description |
|---|---|---|
name | string | The domain name |
registration_date | datetime or null | When the domain was registered |
expiry_date | datetime | Current expiry date |
status | string | One of: active, renewing, expiring, expired, redemption, suspended, verifying |
has_whois_privacy | boolean or null | null if the TLD has no WHOIS privacy |
is_lock_enable | boolean or null | Registrar lock. null if the TLD has no lock |
is_auto_renew | boolean or null | null if the TLD has no auto-renew |
managed_dns | boolean or null | Whether DNS is served by Storefront and manageable through the DNS endpoints |
reseller_has_default_dns | boolean or null | Whether you have a default DNS template configured |
domain_auth_info | string or null | Transfer auth code. null for TLDs that don't use one |
tld_supports_auth_code / tld_supports_locking / tld_supports_contact_privacy / tld_supports_send_auth_code | boolean | Which settings this domain's TLD actually allows — check these before calling an endpoint that would be rejected |
domain_privacy_purchased | boolean | Whether the privacy product has been purchased for this domain |
free_whois_privacy | boolean | Whether privacy is included free for this TLD |
whois_privacy_is_for_individual | boolean | Registry-specific privacy classification |
tld_data | object or null | TLD-specific registrant data where the TLD has any (e.g. se_registration_number, vat_id for .se/.be) |
username | string | Login name of the owning customer |
customer | object | The owning customer — id, email, username, first_name, last_name, status, created_date, last_login_date |
Note: settings a TLD doesn't support come back as
nullrather thanfalse— check thetld_supports_*flags before assuming a setting is simply off.
Domains mid-transfer-in aren't visible here until the transfer completes. Requesting a domain that doesn't belong to your account returns a 403; a domain not in your storefront returns 404.
Get domain contacts
GET /v1/domain/{domain}/contacts
Response: 200 OK — registrant is always present. admin, tech, and billing are omitted (not null) when the registry doesn't hold a separate contact for that role, so check for the key rather than assuming null.
Each contact includes: first_name, last_name, org_name, address1–address3, city, country (2-letter ISO code), postal_code, state, phone, fax, email. The registrant record may carry extra TLD-required fields (for example se_registration_number for .se) — only fields that apply to this domain's TLD are returned.
Update domain contacts
PUT /v1/domain/{domain}/contacts
registrant is required and replaces the current registrant in full. admin, tech, and billing are each left untouched if you omit them.
Registries validate registrant changes, and some TLDs require extra registrant fields (e.g. ca_legal_type for .ca). A rejection comes back as 400 with the registry's reason in extra. Some TLDs also treat a registrant change as a trade requiring approval, so the change may not be visible immediately.
Get the DNS template assigned to a domain
GET /v1/domain/{domain}/dns_template_id
Returns which of your DNS zone templates this domain is set to use, if any. This is the template that POST /v1/domain/{domain}/dns_records/reset restores.
Update the DNS template assigned to a domain
PUT /v1/domain/{domain}/dns_template_id
Points the domain at one of your DNS zone templates, or send null to remove the association. This only records which template the domain should follow — it doesn't rewrite the zone. Call the reset endpoint to actually apply the template's records. The template must be one of yours.
See DNS Templates for how templates are created.
Get current nameservers for a domain
GET /v1/domain/{domain}/nameservers
Returns the nameservers currently delegated at the registry, in registry order — read live from the registry, not from Storefront's own copy.
Update nameservers for a domain
POST /v1/domain/{domain}/nameservers
Replaces the domain's delegation with the nameservers you send — at least two, no duplicates. This is a full replacement, not a merge.
Moving a domain off Storefront's managed DNS stops Storefront serving its zone; the records themselves are kept, so pointing the nameservers back restores them. If the domain is registrar-locked and its TLD allows locking, Storefront unlocks it for the update and re-locks it afterward automatically.
Reassign a domain to another customer ("push transfer")
Reassigns a domain from its current customer to another customer within your own storefront. This is not a transfer to another reseller — both the current and target customer must already belong to you.
POST /v1/domain/{domain}/push_transfer
Request body: the target customer's ID (UUID).
Asynchronous — the call queues the move and responds 200 immediately. Poll the GET endpoint below for the outcome. A domain with an in-flight transfer isn't eligible for another one. Errors: 400 if the domain is mid-transfer or the target customer already owns it; 404 if the domain or customer doesn't exist.
Check a domain reassignment
GET /v1/domain/{domain}/push_transfer
Response: the outcome of the most recent push transfer for this domain — status is one of pending (queued or running), completed, or failed.
Requesting status for a domain with no active or past transfer returns 404, not an empty result.
Update domain settings
Turns auto-renew, registrar lock, and WHOIS privacy on or off for one domain.
PATCH /v1/domain/{domain}/settings
Request fields (all optional — send only what you want to change):
| Field | Type | Description |
|---|---|---|
has_whois_privacy | boolean | Hide the registrant's details in WHOIS. Only for TLDs with tld_supports_contact_privacy |
is_lock_enable | boolean | Registrar lock, blocks transfers away. Only for TLDs with tld_supports_locking |
is_auto_renew | boolean | Renew automatically before the domain expires |
Not every TLD supports every setting — check the tld_supports_* flags on the domain object first. Responds 200 with no body. A rejected change returns 400 with the reason in extra.
Update settings for many domains at once
PATCH /v1/domains/settings
Updates auto-renew, registrar lock, and WHOIS privacy for up to 100 domains in a single request. Each domain is processed independently, so a failure on one doesn't affect the others.
Request: a domains array (1–100 items), each with a domain name and any of is_auto_renew, is_lock_enable, has_whois_privacy.
Response: 200 OK always (even with partial failures) — num_succeeded, num_failed, and a results array with one entry per input domain, in request order, each showing success and, on failure, an error with the reason.
Import a batch of domains
POST /v1/domains/import
Request: an array of domain names (1 or more) that you already hold at OpenSRS. Pulls them into this storefront so they show up in the domain list and can be managed through this API.
Asynchronous — returns a batch ID immediately. Duplicates in the list are collapsed; importing a domain already in the storefront is a no-op for that domain, not an error. If your reseller account hasn't completed the required onboarding steps, the request is rejected with 400.
See Importing Domains for the equivalent UI-based flow.
List your domain portfolio
GET /v1/domains
Every domain in your storefront, filterable and sortable on any column.
Filter syntax: ?<column>=<operator>.<value>. Operators: eq, neq, gt, gte, lt, lte, like, ilike (* wildcard), in, is — prefix any operator with not. to negate it. Repeat a parameter to AND several conditions on the same column.
GET /v1/domains?status=eq.active GET /v1/domains?status=in.(active,expiring) GET /v1/domains?expiry_date=lt.2026-01-01&order=expiry_date.asc
Filterable/sortable columns: name, registration_date, expiry_date, status, username, customer_id, tld, true_tld, created_date, lock_enabled, whois_privacy, autorenew, nameservers, tld_supports_locking, tld_supports_auth_code, tld_supports_send_auth_code.
Note: these column names don't all match the single-domain object's field names above —
lock_enabledhere isis_lock_enableon the domain object,whois_privacyhere ishas_whois_privacy, andautorenewhere isis_auto_renew. Use the list column names when filtering/v1/domains, and the object field names everywhere else.
Sorting: order=<column>.<asc|desc>, comma-separated for multiple columns, with optional .nullsfirst/.nullslast.
Pagination: limit defaults to 25; anything above 100 is clamped to 100 rather than rejected. offset skips rows. Read the Content-Range response header (e.g. 0-24/1337) for the total count and to decide whether to fetch the next page. This is not cursor-based — there's no page token.
Common use cases from filters on this same endpoint:
- Domains expiring soon:
expiry_date=lt.<date> - Domains for a specific customer:
customer_id=eq.<id> - Domains in a given status:
status=eq.<status>orstatus=in.(<status1>,<status2>)
Error responses
Two error shapes appear across these endpoints:
Auth and not-found errors use detail:
{ "detail": "<message>" }
Business validation errors use error_string and extra:
{ "error_string": "domain settings update failed", "extra": "TLD does not support locking" }
| Status | Meaning |
|---|---|
| 400 Bad Request | Constraint violation — registry validation failure, duplicate nameservers, settings not supported by the TLD, onboarding incomplete |
| 401 Unauthorized | Missing or invalid token |
| 403 Forbidden | Domain belongs to another reseller |
| 404 Not Found | Domain, contact, template, customer, or transfer not found |
| 422 Unprocessable Entity | Request failed schema validation |
| 429 Too Many Requests | Rate limit exceeded — check Retry-After header |
Related articles
- API: Managing Customers — create, read, and update customer accounts via API
- API DNS Management Guide — API credentials, authentication, and DNS record management
- DNS Templates — how templates are created and what they control
- Importing Domains — the UI-based import flow this endpoint parallels
- Storefront Nameservers — Shopco vs. custom nameservers
Questions or issues with the API? Contact OpenSRS Support.
How helpful was this article?
Thanks for your feedback!
Do you still need help? If so please submit a request here.