Before you write any code
Base URL https://www.wafra.net/api/reseller/v1
Auth Authorization: Bearer <key>
Money QAR, sent as a JSON number. 45 means 45.00.
The build order
| # | Step | Calls | Spends money? |
|---|---|---|---|
| 01 | Create a key | portal only | no |
| 02 | Read the account | GET /account /balance /ledger | no |
| 03 | Keep the balance | portal only | no |
| 04 | Receive events | POST /webhooks | no |
| 05 | Name check | GET /domains/check | no |
| 06 | Customers and contacts | POST /customers POST /contacts | no |
| 07 | Quote, then register | POST /pricing/quote POST /domains | yes |
| 08 | Renew, transfer, DNS | /renew /transfer /dns/enable | yes |
| 09 | Server catalogue | GET /servers/locations /plans | no |
| 10 | Quote, then create | POST /servers/quotes POST /servers | yes |
| 11 | Operate the machine | /power /backups /mail /dns | no |
| 12 | Resize and renewal | /rescale /renew /cancel | yes |
| 13 | Publish the hostname | GET PUT /servers/hostname | no |
Start
Prove the key, keep the balance in the portal, and listen for the result.
01Create a key
portal only
What it is. The credential for every call. It is created in the portal under API keys, not by an API call.
| Key prefix | Spends | Use for |
|---|---|---|
wf_test_ | test balance | building and testing |
wf_live_ | live balance | production |
What it does. Give the key the scopes it needs: domains and servers. The secret is shown once.
How to implement
- Set the top bar to Current Mode: Test. The portal then shows the test balance, test keys, test names, test servers, and the test ledger.
- Add funds adds any amount to the test domain balance or the test server balance and takes no card.
- Build against
wf_test_. The Sandbox page calls every published path with the test key and shows the panel those calls fill. - When WAFRA approves the company, switch the same code to
wf_live_.
02Read the account
GET /accountGET /account/balanceGET /account/ledger
What it is. These reads confirm the key and show the money.
| Call | Returns |
|---|---|
GET /account | The company and its status |
GET /account/balance | Two wallets. available_qar and held_qar are the domain balance. server_available_qar and server_held_qar are the server balance |
GET /account/ledger?wallet=domain | Top-ups, holds, captures, and releases on the domain wallet |
GET /account/ledger?wallet=server | The same list for the server wallet |
Amounts are JSON numbers in QAR. 45 means 45.00 QAR.
How to implement
- Send
Authorization: Bearer <key>tohttps://www.wafra.net/api/reseller/v1. - Keep the key off the browser.
- If the status is not
active, stop and wait for approval before a live purchase.
03Keep the balance
portal only
What it is. There is no API to add money. The company keeps each balance itself.
| Wallet | Pays for | Read |
|---|---|---|
| Domain | Names | available_qar |
| Server | Machines | server_available_qar |
A wf_test_ key reads and spends the test balances. A wf_live_ key reads and spends the live balances.
How to implement
- Load the live balance in the reseller portal, on Add funds, before you sell.
- Set Current Mode to Test to load any test amount instead.
- WAFRA can also credit the live balance from the desk. Your integration only reads the balance and spends what is already there.
04Receive events
POST /webhooks
What it is. Your HTTPS URL. WAFRA posts the result of a registration, a renewal, a transfer, and a low balance.
Request
POST /webhooks
Authorization: Bearer wf_live_...
Content-Type: application/json
{ "url": "https://yourapp.example/wafra-webhook" }The signing secret is returned once. Save it.
Headers on every delivery
| Header | Value |
|---|---|
X-Wafra-Event | the event name |
X-Wafra-Signature | hex HMAC-SHA256 of the raw body, using your secret |
Verify before you trust
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
return res.status(401).end();
}
res.status(200).end();Answer 2xx quickly, then do your own work.
Events
| Event | Meaning |
|---|---|
domain.registered | Registration succeeded, hold captured |
domain.registration_failed | Registration failed, hold released |
domain.renewed | Renewed. Carries domain_id, not the domain name |
domain.grace | Paid term ended. Renewable for 30 days |
domain.removed | Deleted. This is not a registration result |
domain.transfer.completed | Transfer in finished |
domain.transfer.failed | Transfer failed |
domain.eligibility.expiring | .qa document expiring. Fields: domain_id, domain, customer_id, expires_on, days_left, stage (30 or 7) |
dns.grace | WAFRA DNS charge failed. 5 days to pay |
dns.removed | DNS zone and records deleted |
server.grace | Machine is off for 7 days |
server.removed | Machine deleted |
balance.low | Domain balance at or under the company threshold, or an automatic domain renewal could not hold the price |
topup.captured | Top-up received |
Amounts in the body are QAR. dns.grace and dns.removed are the DNS anniversary, not the domain expiry.
Domains
Check, quote, then register. Renew and transfer use the same quote. An unpaid name can be renewed for 30 days, then it is deleted. An expired .qa document suspends the name for 15 days.
05Check the name
GET /domains/check?domain=
What it is. The check asks if the name is free.
| Call | Returns | Charges? |
|---|---|---|
| Zones and the QAR price for register, renew, and transfer | no | |
GET /domains/check?domain= | Whether the name can be registered, and the one-year price | no |
A zone that is not on this company comes back as unsupported.
06Customers and contacts
POST /customersPOST /contacts
What it is. The customer is the person you are selling to. The contacts are the four records the registry stores on the name.
| Call | Stores a password | Returns | Spends |
|---|---|---|---|
POST /customers | yes | customer id | no |
POST /auth/register | yes | customer id, access token, refresh token | no |
POST /auth/login | no | access token, refresh token | no |
POST /auth/refresh | no | access token, refresh token | no |
Both create calls take email, a password of at least 8 characters, and name. The access token lasts 15 minutes.
A customer token can list and read that customer's servers, poll readiness, power them, run backups, and manage Coolify domains, DNS, and mailboxes. Ordering, resize, the billing cycle, renewal, and deletion stay on the company key.
Contact fields
All of these are required. state is optional.
| Field | Rule |
|---|---|
first_name, last_name, organization, address, city | English characters only |
email | required |
phone | Includes the country code. +97470202010 and +974.70202010 are both accepted. The API stores +974.70202010 |
postal_code | At least 4 digits when it is numbers only, or 2 characters when it contains a letter |
country | Two-letter code |
customer_id | The buyer id from POST /customers. That contact belongs to that buyer |
first_name and last_name are joined into one name for .qa. One contact is created on both .qa and the other registry, and the same contact id is used for either name. One contact id can fill more than one role.
WAFRA assigns the contact id. The reseller does not. PATCH /contacts/{id} can change an admin, tech, or billing record. A contact that is the registrant on a name cannot be edited or deleted. Point the domain at a different verified contact with PUT /domains/{id}/contacts.
How to implement
- If buyers already sign in on your side, call
POST /customersfrom your server, keep the password you generated, and store only the returned id. Pass that id ascustomer_id. - If you want WAFRA to sign the buyer in, use
POST /auth/registerand store the refresh token on your server. - Save the four contact ids and reuse them.
- A contact missing
address,city,postal_code,organization, or any other required field is refused, on create and again when it is used as the registrant. POST /contacts/{id}/verifyasks the registry to email that contact..qadoes not send its own email.POST /contacts/{id}/activatesends the code and marks the contact verified only when the registry accepts it. A test key returns the code in the verify response and sends no email.
07Quote, then register
POST /pricing/quotePOST /domains
What it is. The quote freezes the wholesale price for 5 minutes. The register call spends it.
The flow
POST /pricing/quote ─► quote_id (valid 5 min)
│
▼
POST /domains (quote_id + Idempotency-Key + 4 verified contacts)
│
├─ 201 ─► name registered, hold captured
│ └─► .qa only: upload document next
├─ 422 before hold ─► no money moved
├─ 422 registry_failed ─► hold released, money is back
└─ 503 settlement held ─► DO NOT RETRY. Wait for the event.Step 1: quote
| Field | Required | Notes |
|---|---|---|
action | yes | register |
domain | yes | |
years | yes | 1 to 10 |
wafra_dns | no | true adds WAFRA DNS at 24 QAR a year |
The response id and quote_id are the same value, with amount_qar and expires_at.
Step 2: register
| Field | Required | Notes |
|---|---|---|
quote_id | yes | From step 1. Older than 5 minutes means quote again |
domain, years | yes | Must match the quote |
contacts.registrant contacts.admin contacts.tech contacts.billing | yes | All four must already be verified |
wafra_dns | no | Must equal the quote, or omit it. A mismatch is refused before any hold |
eligibility_entity eligibility_type eligibility_value | .qa family | See below. Send them before the hold |
extra | some TLDs | See Reference. A checkbox extra is true. A select extra must be one of the listed values. An extension that is not offered is refused |
Header Idempotency-Key | yes | New key per new attempt. The key is stored for the company and is not expired by the API |
For .qa, .com.qa, and .net.qa, each hostname is turned into a nameserver id before the name is registered, and a missing host is created. The Qatar registry does not accept the hostname on the domain. An empty id list leaves the name inactive. WAFRA DNS publishes ns5.wafracloud.net and ns6.wafracloud.net.
WAFRA assigns the domain id. For .qa, .com.qa, and .net.qa that id is the numeric id stored at the registry, and renew uses that same id. A dom_ id is used only when the registry has no number. The reseller does not choose domain, contact, nameserver, or server ids. The reseller chooses customer_id. The name is also stored on the reseller's WAFRA user, so WAFRA can open it the same way as a retail name. Retail card renewal and retail invoices do not charge it. Renewal spends the reseller balance when POST /domains/{id}/renew is called.
Results
| Response | Meaning | Money | What you do |
|---|---|---|---|
201 | Registered | captured | Upload the document if .qa |
422 contact_unverified | Contact not verified | none moved | Verify, then retry with a new key |
422 eligibility_required | Missing eligibility | none moved | Fix the body |
422 registry_failed | Registry refused, after the hold | released | Money is back |
503 settlement: held | Outcome pending | held | Do not retry. Wait for the event |
`.qa`, `.com.qa`, and `.net.qa` eligibility
| Zone | eligibility_entity | eligibility_type |
|---|---|---|
.qa | person | Qatar ID or Residency Permit |
.qa | company | Trade License/CR |
.com.qa, .net.qa | company only | Trade License/CR |
eligibility_value is the number printed on the document: the CR number, the licence number, or the ID number.
Name matching
- An individual contact must be the English name on the card, in the same words and the same order.
- A company contact must be the legal entity name. A legal-form suffix may differ.
- For a Qatar ID or a Residency Permit, the
.qalabel must be that English name with spaces and hyphens removed..com.qaand.net.qado not use that label rule. - Trademark is not accepted. Do not send a document link.
Document lifecycle
| When | What happens |
|---|---|
| File missing | GET /domains/{id}/documents returns on_file false and blocked true. Renew is refused with eligibility_required and no money is held |
| 30 days and 7 days before expiry | domain.eligibility.expiring, stage is 30 or 7 |
| Expiry date | Nameservers become nssuspend1.wafracloud.net and nssuspend2.wafracloud.net for 15 days |
| Day 16 | The name is set for deletion |
08Renew, transfer, and DNS
POST /domains/{id}/renewPOST /domains/transferPOST /domains/{id}/documentsPOST /domains/{id}/dns/enable
What it is. Each of these is the same pattern as registration: quote, then the billable call.
| Job | Quote action | Then call | Money |
|---|---|---|---|
| Renew | renew | POST /domains/{id}/renew | Domain balance. The renew quote does not include WAFRA DNS |
| Transfer in | transfer_in | POST /domains/transfer with quote_id, domain, auth_code, and the same eligibility and contacts a registration needs | .qa, .com.qa, and .net.qa quote at 0 QAR. Any other transfer stays held until it finishes |
| WAFRA DNS | dns | POST /domains/{id}/dns/enable | 24 QAR a year from the day it is turned on, not from the domain expiry |
The old path POST /domains/{id}/transfer is the same call as POST /domains/transfer. Upload the file after the name exists. Do not send a link.
| Call | What it does |
|---|---|
POST /domains/{id}/transfer/complete | Finish a waiting .qa transfer with the new registrant eligibility |
POST /domains/{id}/internal-transfer | Move a .qa name between two of your own customers. No transfer code |
POST /domains/{id}/transfer-out | Sets transfer_status to outbound and returns the auth code. Calling it again returns the same code. For .qa the registry emails that code to the registrant |
GET /domains/{id}/auth-code | Reads that code and does not change the status |
GET /domains/transfers | Every row with a transfer status, including a finished transfer-in, a transfer-out, and an internal move |
PUT /domains/{id}/contacts | Assigns different verified contacts. It does not edit the contact record. Changing the registrant on .qa, .com.qa, or .net.qa needs the new holder's eligibility |
POST /domains/{id}/dns | Adds a record with type, host, value, and ttl, only while DNS is on and not in the 5-day window |
PATCH or DELETE /domains/{id}/dns/{recordId} | Edits or removes a record while WAFRA DNS is on |
POST /nameservers then PUT /domains/{id}/nameservers | Nameservers are free. For .qa that PUT, and registration, turn each hostname into a nameserver id and create a missing host. An empty id list leaves the name inactive |
PATCH /domains/{id} | Can set name_servers, locked, or auto_renew |
POST /domains/{id}/documents | Multipart form data: file, eligibility_entity, eligibility_type, eligibility_value. One file is enough |
GET /domains/{id}/documents | Reports on_file |
POST /domains/{id}/eligibility | Does not store a file. For .qa, .com.qa, and .net.qa it is refused |
DNS anniversary
POST /domains/{id}/dns/enable sets dns_paid_until one year ahead for each year on the quote. POST /domains/{id}/dns/disable turns billing off and leaves the zone in place until that paid date. On the paid date the balance is charged again if DNS is still on. If the balance is short, or DNS was turned off, there are 5 days to pay. Paying calls dns/enable again and turns the zone back on. On the 6th day the zone and its records are removed.
How to implement
- A new domain is stored with auto-renew on.
PATCHauto_renewfalse when the domain balance should not renew it. The same switch is on the domain page. A server starts on, andPOST /servers/{id}/auto-renewturns it off. - None of these charges a card. A short balance, or auto-renew left off, puts the name in a 30-day grace after the expiry date, and the server off for 7 days. Power on is refused until Renew.
- The renewal run is once a day at 03:00 UTC. It looks 14 days ahead and takes one year from the domain balance. If the balance cannot hold that price,
balance.lowfires that day and again on each later day the hold still fails. That webhook is not deduped. WAFRA DNS is not part of that charge. - A short balance before the expiry only warns. The current paid term stays up. If it is still unpaid when the 30 days end, the name is deleted at the registry. Nameserver, lock, and DNS changes are refused until
POST /domains/{id}/renewsucceeds. - For
.qa,.com.qa, and.net.qa, renew is refused until a current eligibility document is stored, and an expired document blocks every change, including renew. The renewal sends the stored type and the number on that document. The registrant must match the name on the document. If the current file is not stored, the nameservers becomenssuspend1.wafracloud.netandnssuspend2.wafracloud.netfor 15 days. On the 16th day the name is deleted at the registry.
Servers
Quote, create, then operate. Renewal comes out of the server balance. An unpaid server is off for 7 days, then it is deleted.
09Read the server catalogue
GET /servers/locationsGET /servers/plans
What it is. Choose a location first. The plan list is the machines this company can sell there, at the price set for this company.
| Fact | Rule |
|---|---|
| Price | One price in QAR, monthly and yearly. A company price is what the server balance pays. A plan with no company price uses the current selling price |
| Listed machines | Only machines that can be provisioned immediately. Dedicated servers are hidden, and the dedicated calls are not on this API. The cx63 plan is hidden. A plan the company has hidden is omitted |
| Show or hide | POST /servers/plans/{plan_id} with shown true puts a plan back, and shown false hides it. Send the plan id you already know |
| Location | GET /servers/locations returns the ids. GET /servers/plans requires location |
hel1 | Helsinki catalogue only. A region plan is not in that list |
| Any other location | That region catalogue only. A Helsinki plan is not in that list |
| Disk, CPU, memory | Come from the plan. Each plan includes disk_gb |
| Images | GET /servers/images returns image ubuntu-22.04 and panels hestiacp and coolify. The call does not charge. The servers scope is required |
How to implement
- Read the locations first, then
GET /servers/planswith that location, then the images. - Use those ids exactly. Cache the plan list for the page, and read it again before a quote.
- The price in the list is the price the server balance will pay.
10Quote, then create
POST /servers/quotesPOST /servers
What it is. The quote freezes this company price for five minutes. The create call holds it on the server balance and starts the machine.
| Field | Required | Notes |
|---|---|---|
plan_id | yes | From the catalogue for that location |
billing_cycle | yes | monthly or yearly |
location | yes | An id from GET /servers/locations. Helsinki plans use hel1 |
image | yes | Must be ubuntu-22.04 |
panel | yes | hestiacp or coolify. Coolify is rejected on a machine that is too small |
hostname_parent | no | Omit to use the parent saved in Settings, or the buyer parent when customer_id is sent. An empty string forces wafracloud.net. A full hostname is refused |
customer_id | no | Uses that buyer parent when hostname_parent is omitted |
hostname | no | Do not send it. WAFRA assigns vps and six new letters, then the parent. The six letters change on every quote |
disk_gb, cpu, ram, memory, storage | no | Do not send them. Disk, CPU, and memory come from the plan |
The nameservers are ns1 and ns2 on that new host. Step 13 is the setup. The quote field publication says who writes the zone. publication external means the name is not published. The quote response includes the hostname WAFRA assigned.
POST /servers takes quote_id and an optional customer_id. Idempotency-Key is required. The server balance holds the cycle price plus any setup. The hold stays until the tunnel is up. The server is marked ready only then, and the login is emailed to the company and, when customer_id was sent, to that customer. If the tunnel is still down one hour after the order, status becomes error and the hold is released. A test key returns a fake server already ready and never starts a real machine.
How to implement
- Send the location, image, and panel you read from the catalogue calls. Do not invent a location or an image.
- Save the server id from the
201.settlementishelduntil the machine is ready. - Poll
GET /servers/{id}/readinessuntilreadyis true. Keep polling. The wait is at most 1 hour. Payment does not mean the machine is ready.ready,tunnel,wait_until, andseconds_remainingare on that response. GET /servers/{id}returnshostname,url,address,location,plan_id,root_username,root_password,panel_username,panel_email,panel_password,storage_gb,ram_gb,cpu_cores,backup_cap_gb,ready, andwait_until.root_usernameisroot. On Coolify,panel_usernameandpanel_emailare the email address, andpanel_passwordis the password. On Hestia,panel_usernameis the panel user. The list omits the passwords.- There is no webhook for a server becoming ready.
11Operate the machine
GET /servers/{id}/readinessGET /servers/{id}/detailsPOST /servers/{id}/powerPOST /servers/{id}/backupsGET /servers/{id}/domainsGET /servers/{id}/dnsPOST /servers/{id}/mail
What it is. Power and backups are the day-to-day controls. They do not charge the balance. The panel address and the root login are already on the server read. There is no terminal call.
| Control | Send | Charges? |
|---|---|---|
| Power | action on, off, or reboot | no |
| Backups | Copies are stored on Backblaze. Losing the machine does not delete copies already uploaded | no |
op list, connection, create, password, or delete | no |
Opening the server lists the copies and does not start a backup. The first time the copy store is missing, that read prepares it and starts the first copy. A later read does not start another copy. One copy is taken each day. Five copies are kept.
| Panel | Restore |
|---|---|
Hestia, hestiacp | op restore with snapshot_id, or op restore-account with user and snapshot_id. Account reads are account-snapshots, account-snapshot-inventory, account-snapshot-mailboxes, and account-restore-status |
Coolify, coolify | Whole server is op restore. Platform ops start with coolify-platform-. Domain ops start with coolify-domain- and send domain. App ops start with coolify-app- and send uuid |
| Shared reads | get-status, snapshots, get-usage, run-now, verify, and restore-status |
An operation from the other panel is refused. A customer token can read the server, poll readiness, power it, run backups, and manage Coolify domains, DNS, and mailboxes. Ordering, resize, the billing cycle, renewal, and deletion stay on the company key.
Coolify domains, DNS, and mail
On a Coolify server, add the domain before a mailbox. POST /servers/{id}/domains sends domain and creates the DNS zone, the mail domain, the site, and the certificate. The zone already holds the address records, mail, webmail, and an MX record for mail.domain. The response nameservers are ns1 and ns2 on the server hostname.
| Call | Body |
|---|---|
GET /servers/{id}/dns?domain= | Lists the zone |
POST /servers/{id}/dns | domain and record, with record.name, record.type, record.value, and optional record.priority and record.ttl |
PUT /servers/{id}/dns | The same record plus old_name and old_type |
DELETE /servers/{id}/dns?domain=&name=&type= | Removes that record |
DELETE /servers/{id}/domains?domain= | Removes that domain |
GET or POST /servers/{id}/mail | Mailboxes only. create and password also send email and a password of at least 6 characters |
Types are A, AAAA, CNAME, MX, TXT, SRV, and CAA. NS records and the ns1 and ns2 records are not listed. A write that names them is refused, because changing them can take the server offline. A TXT value is returned in full. A semicolon inside the value is kept. Send subdomain_of when the name sits under a domain that is already there. Those records are written into the parent zone.
GET /servers/{id}/details returns the hostname, the address, ns1 and ns2 on that hostname, the nameserver address, and the uptime once the server is active. Readiness stays for a machine that is not active yet.
IMAP is mail.domain port 993 and SMTP is mail.domain port 465. Hestia domains, DNS, and mail stay in the Hestia panel, and these calls are refused there. There is no mail security, filter, forwarder, or migration call. There is no call for a root password reset, a rebuild, reverse DNS, metrics, a browser console, a firewall, rescue, an ISO, or a static address. WAFRA sets the reverse record when the machine is created.
How to implement
- Call power, backups, and mail only after readiness says
readyis true. - While a server is in the 7-day grace, power on and reboot are refused until it is renewed.
- Show the panel url and the root login in your own panel. The customer does not need a WAFRA login.
- The reseller portal server page uses these same calls. The Coolify login is the email and the password. It does not include those mailboxes. A mailbox saved in the portal is the mailbox
GETorPOST /servers/{id}/mailreturns.
12Resize and renewal
POST /servers/{id}/rescale/quotePOST /servers/{id}/rescalePOST /servers/{id}/renew
What it is. A larger plan charges the remaining days of the current cycle. Renewal charges the server balance on the renewal date.
| Field on the quote | Meaning |
|---|---|
charge_qar | The price difference for the days left in the cycle. A yearly cycle uses twelve times the monthly difference, then the share of the year that is left |
is_downgrade | A downgrade returns charge_qar 0 and does not credit the wallet |
days_remaining | Days left in the cycle |
next_qar | The cycle price stored after the change |
A Helsinki machine stays on the Helsinki catalogue. The new plan must use the same processor family and the same disk type. Shared and dedicated CPU plans in that family are allowed. Send keep_disk. Leave it out or send true to keep the current disk, which is what the WAFRA card does. Send false only when the new plan disk is larger and that disk should grow. That growth is permanent and later plans must be at least that size. Leave disk_gb out. A smaller disk is refused.
A region machine stays in the same location and the same machine family. Read GET /servers/plans for that location and send one of those plan ids. Send disk_gb. Omit it to keep the current disk. A value below the current disk is refused. The disk can only grow. Leave keep_disk out. Sending the other machine field is refused. The same plan with the same disk is refused.
The machine stops, the change is applied, and it starts again. POST /servers/{id}/rescale takes the same body. Send Idempotency-Key when charge_qar is above 0.
| Call | Effect |
|---|---|
POST /servers/{id}/billing-cycle | billing_cycle monthly or yearly, a different value from the one stored. It does not read Idempotency-Key. The same server and the same new cycle are one charge. A cheaper cycle changes the stored price and does not refund |
POST /servers/{id}/auto-renew | A new server is stored with auto-renew on. This sets it on only when enabled is the boolean true |
POST /servers/{id}/renew | Charges the next period from the server balance. It is the only call that starts a machine that was turned off for a missed renewal |
POST /servers/{id}/cancel | Deletes the machine now, sets status cancelled, and stops renewal. WAFRA then removes the zone rows and the registry glue it created. An outside parent is left unchanged. The reverse record goes away with the machine. The period already captured is not refunded |
There is no card invoice for a reseller server. There is no manage-server call.
13Publish the server hostname
GET /servers/hostnamePUT /servers/hostnamePATCH /customers/{id}
What it is. Every server gets a name: vps plus six new letters plus . plus the parent. Its nameservers are ns1 and ns2 on that same name.
Choose a parent
| Option | You send | Who writes the zone | Use when |
|---|---|---|---|
| WAFRA zone | hostname_parent "" | WAFRA, on wafracloud.net | Almost always. This does not use the domain wallet |
| Company domain | omit hostname_parent | WAFRA if the domain is on this account with WAFRA DNS paid. Otherwise you | The parent saved in Settings should be the name on every new server |
| Customer domain | the customer's domain | Same rule as a company domain | This order should use that customer's own domain |
Read `publication` on the quote
publication | automatic | Meaning |
|---|---|---|
wafra | true | WAFRA writes everything on wafracloud.net |
account | true | Parent is on this account with WAFRA DNS. WAFRA writes the zone |
external | false | WAFRA writes nothing in the zone. Your code must |
Who does what
| WAFRA | You | |
|---|---|---|
| Assign hostname and nameserver names | yes | |
| Assign the IP address | yes | |
Write the zone, wafracloud.net or an account parent with DNS paid | yes | |
| Write the zone, outside parent | yes | |
Create glue at an outside registrar (glue yours) | yes. No API call exists | |
glue registry | yes, even when publication is external | |
| Set reverse DNS to the hostname | yes, for every parent | |
| Check that the name resolves | yes | |
| Keep the parent's WAFRA DNS paid | yes, domain wallet |
POST /nameservers is only for a domain this account holds at Qatar or CentralNic. automatic false means the zone is not written.
The five records, for an `external` parent
Read them from GET /servers/{id}/details once address is present. Rows are relative to the zone when publication is account, and relative to the parent when publication is external. Example with parent example.com, label vpsabcdef, IP 203.0.113.10:
vpsabcdef A 203.0.113.10
ns1.vpsabcdef A 203.0.113.10
ns2.vpsabcdef A 203.0.113.10
vpsabcdef NS ns1.vpsabcdef.example.com.
vpsabcdef NS ns2.vpsabcdef.example.com.WAFRA uses TTL 3600. The response does not include a TTL. The sandbox address is 203.0.113.10, so you can test the record shape. The sandbox writes nothing to any registrar.
What can take the names down
Good to know
- Settings does not rename a server that already exists. A new quote mints a new name. Changing a domain buyer does not rewrite servers that already exist.
PUT /servers/hostnamereturns the same fields asGET.PATCH /customers/{id}withhostname_parentsaves a buyer parent. An empty string clears it. Send thatcustomer_idon the quote.- A parent that belongs to a different buyer is refused on the quote and on PATCH. The error names that buyer and says to send that
customer_id, or to use a company domain. - Create inherits the quote's buyer. A different
customer_idon create is refused before the hold. - Cancel removes only the five rows and the
ns1andns2hosts WAFRA created. It does not delete the parent zone. An outside parent is left for the reseller. A row you edited to another address stays.
How to implement
- Use
wafracloud.netunless the DNS side is automatic. Calling the server API is not that automation. - Read
publication,automatic,glue, anddns_graceon the quote.publicationwafraandpublicationaccountare written by WAFRA.publicationexternalmeans the checkout writeshostname_recordsand, whenglueisyours, the host records at the registrar. - Offer three choices. Omit
hostname_parentfor the company domain saved in Settings. Sendhostname_parentas an empty string for the WAFRA zone,wafracloud.net. Send the customer's domain for a customer domain. Do not sendhostname. - When
GET /servers/{id}/detailshas an address, the fivehostname_recordsare the rows to copy. Do this in the same code that creates the server whenpublicationisexternal.
Reference
Contacts
A contact uses the same fields a retailer contact uses. All of these are required: first_name, last_name, email, phone, organization, address, city, postal_code, country.
first_name, last_name, organization, address, and city use English characters only. phone includes the country code. +97470202010 and +974.70202010 are both accepted. The API stores the phone as +974.70202010. A .qa contact is sent to the registry in that form, with first_name and last_name joined into one name. country is the two-letter code, such as QA. postal_code accepts letters or numbers. A code with a letter needs at least 2 characters. A numbers-only code needs at least 4 digits.
state is optional. The stored name is first_name and last_name together.
One contact is created on both .qa and the other registry. Verification is required because that same contact is used for both. POST /contacts/{id}/verify emails the address on the contact. The other registry sends that email. .qa does not send its own. POST /contacts/{id}/activate confirms the code from that email. Registration, transfer in, and an internal transfer refuse until registrant, admin, tech, and billing are verified. The error code is contact_unverified and no money is held.
Documents the reseller keeps
A .qa, .com.qa, or .net.qa name does not take a document link. After the name exists, POST /domains/{id}/documents uploads the file. That call reads it and stores it under the domain name. One file is enough. A Qatar ID or Residency Permit can include the front and the back in that same photo or PDF.
.qa, .com.qa, and .net.qa
These three need eligibility_entity and eligibility_type before the balance is held. eligibility_entity is person for an individual, or company for a company. .com.qa and .net.qa are company only. An individual uses Qatar ID or Residency Permit. A company uses Trade License/CR. Trademark is not accepted. The file is uploaded after the name exists. The reader accepts only a Commercial Registration, a Commercial License, a Qatar ID, or a Residency Permit, and only when that file matches the type and has not expired. A link is not stored. .com.sa is a different mechanism: it sends an https url, not this file upload. The name is registered and paid before the file exists. If the upload is refused, upload again. No suspension starts until a file with an expiry date is stored. Renew is refused until a current file is stored. error.code is eligibility_required and no money is held. GET /domains/{id}/documents returns on_file false and blocked true while the file is missing. The reseller is notified 30 days before the document expires, and again 7 days before. The webhook is domain.eligibility.expiring and stage is 30 or 7. On the expiry date the nameservers become nssuspend1.wafracloud.net and nssuspend2.wafracloud.net. There are 15 days to upload a current file. On the 16th day the name is set for deletion. A new name, and a name that has just transferred in, stays locked for 60 days. PATCH locked false, POST /domains/{id}/transfer-out, and GET /domains/{id}/auth-code return error.code domain_locked until that date, in the API and in the portal. Transfer out is refused for those 60 days. For .qa, .com.qa, and .net.qa the registrar lock is not set, so this API refuses the transfer itself. For every other name the registrar transfer lock is also set at registration. A name that reaches the end of its paid term without a renewal stays for 30 days. Only renewal is accepted. The nameservers are not changed for that reason. After 30 days the name is deleted at the registry.
.qa accepts Trade License/CR, Qatar ID, and Residency Permit. Trademark is not accepted. .com.qa and .net.qa accept Trade License/CR. Qatar ID, Residency Permit, and Trademark are refused on those two.
An individual, for Qatar ID or Residency Permit: upload one file. The front and the back of the card can be in that same file. The number is read from the file. Send eligibility_entity person.
A company, for Trade License/CR: upload one file. Do not send a link. The file is read for the licence or CR number. Send eligibility_entity company.
.com.sa
Send documents with kind sa_trademark or sa_incorporation and an https url. A trademark certificate must be for this exact name. An incorporation certificate is the local-presence alternative. Store the file. WAFRA can ask for it.
Extensions that are not offered
A check returns supported false. Register and transfer are refused. .adult, .adult.ht, .porn, .xxx, .sex, .sexy, .webcam, .casino, .bet, .poker, .lotto, .bingo, .gay, .lesbian, .il, .co.il, .org.il, .net.il, .ac.il, .gov.il, .muni.il, .cannabis, .vodka, .wine, .beer.
Extensions that cannot be transferred in
.cf, .gq, .hu. Registration can still be quoted when the extension is on this company catalogue.
Extensions that need extra fields
Send extra on register. Send it on transfer-in as well when the note below says so. The keys are the same keys a retailer form collects.
.law
Only licensed/accredited legal professionals may register .law domains. Required on register and on transfer-in. Your accreditation details will be sent to the registry for verification. Registration may be delayed or rejected if verification fails.
- extra.accreditationBody (text, required). Accreditation Body. Name of the body that accredited you
- extra.accreditationId (text, required). Accreditation / License ID. Your unique accreditation or bar number
- extra.accreditationYear (text, required). Year of Accreditation.
- extra.jurisdictionCountry (text, required). Jurisdiction Country. 2-letter country code
- extra.jurisdictionProvince (text, optional). Jurisdiction Province/State.
.abogado
Only licensed/accredited legal professionals may register .abogado domains. Required on register and on transfer-in. Your accreditation details will be verified by the registry.
- extra.accreditationBody (text, required). Accreditation Body.
- extra.accreditationId (text, required). Accreditation / License ID.
- extra.accreditationYear (text, required). Year of Accreditation.
- extra.jurisdictionCountry (text, required). Jurisdiction Country.
.aero
Only aviation industry members may register .aero domains. You need an ENS Auth ID from information.aero. Required on register and on transfer-in. Your ENS credentials will be verified with the .aero registry. Apply for an ID at https://information.aero if you don't have one.
- extra.ensAuthId (text, required). AERO ENS Auth ID. Apply at https://information.aero
- extra.ensAuthKey (text, required). AERO ENS Auth Key.
.travel
Registrant must be part of the travel industry. Required on register and on transfer-in. The registry will verify your travel industry affiliation.
- extra.travelUin (text, optional). Travel UIN (Unique ID Number). If you have a Travel UIN, enter it
- extra.travelIndustry (checkbox, required). Travel Industry Confirmation. I confirm the registrant is part of the travel industry
.coop
Only cooperative entities may register .coop domains. Required on register and on transfer-in. The .coop registry will verify that your organization meets cooperative eligibility requirements after submission.
- extra.coopEligibility (checkbox, required). Cooperative Eligibility Confirmation. I confirm this organization meets .coop eligibility requirements
.nyc
At least one contact must have a valid physical address in New York City. Required on register and on transfer-in. The registry will verify that the specified contact has a valid NYC address.
- extra.nycContact (select, required). NYC Contact Role. One of: OWNER, ADMIN, TECH, BILLING. Which contact has the NYC address?
.swiss
Requires a Swiss Enterprise Identification Number (UID). Required on register and on transfer-in. The Swiss registry (BAKOM) will verify your UID after submission. This process may take several days.
- extra.swissUid (text, required). Swiss Enterprise UID. Unternehmens-Identifikationsnummer
- extra.intendedUse (text, required). Intended Use of Domain.
.zuerich
Must be a Zurich public authority or a legal entity registered in the Canton of Zurich Commercial Register. Required on register and on transfer-in. Your UID will be verified against the Canton of Zurich Commercial Register (zefix.ch).
- extra.zuerichUid (text, required). UID Number (or "PUBLIC"). Check your UID at https://www.zefix.ch
- extra.zuerichAgreement (checkbox, required). Canton of Zurich Agreement. I accept the local laws of the Canton of Zurich (TLDV ordinance)
.us
Registrant must have a connection to the United States. Required on register and on transfer-in. The .us registry enforces nexus requirements. Non-compliant registrations may be suspended.
- extra.usNexusPurpose (select, required). Nexus Purpose. One of: P1, P2, P3, P4, P5. Purpose of domain registration
- extra.usNexusCategory (select, required). Nexus Category. One of: C11, C12, C21, C31, C32. Your connection to the United States
- extra.usNexusValidator (text, optional). Country Code (for C31/C32). Required if nexus category is C31 or C32
.au
Registrant must demonstrate Australian eligibility (ABN, ACN, or Trademark). Required on register and on transfer-in. The .au registry will verify your eligibility details.
- extra.auIdType (select, required). ID Type. One of: ABN, ACN, TM, OTHER.
- extra.auIdNumber (text, required). ID Number.
- extra.auRelationType (select, required). Registrant Type. One of: Company, RegisteredBusiness, SoleTrader, Partnership, TrademarkOwner, CitizenResident, IncorporatedAssociation, NonProfitOrganisation, Charity.
.com.au
Registrant must demonstrate Australian eligibility (ABN, ACN, or Trademark). Required on register and on transfer-in. The .au registry will verify your eligibility details.
- extra.auIdType (select, required). ID Type. One of: ABN, ACN, TM, OTHER.
- extra.auIdNumber (text, required). ID Number.
- extra.auRelationType (select, required). Registrant Type. One of: Company, RegisteredBusiness, SoleTrader, Partnership, TrademarkOwner, CitizenResident.
.ca
Registrant must meet Canadian Presence Requirements (CPR). Required on register and on transfer-in. CIRA will verify your Canadian presence status.
- extra.caLegalType (select, required). Legal Type. One of: CCO, RES, ABO, ASS, CCT, EDU, GOV, HOP, INB, LAM, LGR, MAJ, OMK, PLT, PRT, TDM, TRD, TRS. Your connection to Canada
.eu
Registrant must be within the European Union, EEA, or have EU citizenship. Required on register and on transfer-in. EURid will verify your EU connection. Non-EU registrants can use trustee service.
- extra.euRegistrantCitizenship (text, optional). EU Country of Citizenship/Residence. EU/EEA country code, or leave blank if using trustee
- extra.euAcceptTrustee (checkbox, optional). Use Trustee Service (if not EU-based). If you are not based in the EU, a local trustee can be used
.it
Requires Italian entity information or trustee service for non-EU registrants. Required on register and on transfer-in. The .it registry (NIC.it) will verify your identity. Non-EU registrants must use a trustee service.
- extra.itEntityType (select, required). Entity Type. One of: 1, 2, 3, 4, 5, 6, 7.
- extra.itPin (text, required). Italian Tax Code (Codice Fiscale) / VAT. Your Italian fiscal code or VAT number
- extra.itAcceptTrustee (checkbox, optional). Use Trustee Service (if non-EU). Non-EU registrants require a local trustee
.de
Requires a German administrative contact address or trustee service. Required on register and on transfer-in. DENIC requires a German admin-C address. Non-German registrants can use the trustee service.
- extra.deAcceptTrustee (checkbox, optional). Use German Trustee Service. If you don't have a German address, a local trustee will be used
.fr
EU citizens/residents or entities can register. Non-EU registrants can use trustee service. Required on register and on transfer-in. AFNIC may verify your identity. Non-EU registrants must use a trustee.
- extra.frAcceptTrustee (checkbox, optional). Use French Trustee Service. If you are not based in the EU, enable trustee service
.es
Requires Spanish identification details. Required on register and on transfer-in. The .es registry will verify your ID information.
- extra.esOwnerIdType (select, required). Owner ID Type. One of: 0, 1, 2, 3.
- extra.esOwnerId (text, required). Owner ID Number.
.uk
Requires corporate type and registration number for organizations. Required on register and on transfer-in. Nominet may verify your corporate details.
- extra.ukCorporateType (select, optional). Corporate Type. One of: LTD, PLC, LLP, IP, RCHAR, SCH, GOV, STAT. Required for organizations
- extra.ukCorporateNumber (text, optional). Company Registration Number. Companies House registration number
.co.uk
Requires corporate type and registration number for organizations. Required on register and on transfer-in. Nominet may verify your corporate details.
- extra.ukCorporateType (select, optional). Corporate Type. One of: LTD, PLC, LLP, RCHAR.
- extra.ukCorporateNumber (text, optional). Company Registration Number.
.ltda
Requires business authority and license information for Brazilian limited companies. Required on register and on transfer-in. The registry will verify your company license details.
- extra.ltdaAuthorityName (text, required). Issuing Authority Name.
- extra.ltdaLicenseNumber (text, required). License Number.
.srl
Requires business authority and license information for SRL companies. Required on register and on transfer-in. The registry will verify your company license details.
- extra.srlAuthorityName (text, required). Issuing Authority Name.
- extra.srlLicenseNumber (text, required). License Number.
.berlin
Requires local presence in Berlin, Germany, or trustee service. Required on register. The registry will verify the Berlin presence. Trustee service creates a local contact on your behalf.
- extra.berlinTrustee (checkbox, optional). Use Berlin Trustee Service. Enable if you don't have a Berlin address
.bayern
Requires local presence in Bavaria, Germany, or trustee service. Required on register. The registry will verify the Bavarian presence.
- extra.bayernTrustee (checkbox, optional). Use Bavaria Trustee Service.
.hamburg
Requires local presence in Hamburg, Germany, or trustee service. Required on register. The registry will verify the Hamburg presence.
- extra.hamburgTrustee (checkbox, optional). Use Hamburg Trustee Service.
.ruhr
Requires local presence in the Ruhr region, Germany, or trustee service. Required on register. The registry will verify the Ruhr region presence.
- extra.ruhrTrustee (checkbox, optional). Use Ruhr Trustee Service.