Automate reselling and managing LJPc hosting and domain names.
A REST API that returns JSON. Register and transfer domains, manage DNS, order and provision hosting and manage client sites, exactly like the reference boilerplate does.
The LJPc reseller API lets you sell and manage domain names and hosting fully automatically. It is a REST API that returns JSON over HTTPS.
All requests go to the base URL https://ljpc-hosting.nl with paths under /api. Hosting paths live under /api/hosting/v1, the order and product paths under /api/v1. All amounts are the price your account pays us, including your valuepack, excluding VAT; on top of that, as a reseller you set your own retail price for your customer.
Authentication
Every request authenticates with the X-API-Key header. There is no bearer token or cookie involved. A missing or unknown key returns HTTP 401 with an envelope body.
Almost every endpoint wraps its payload in the standard envelope. Read .data for the payload on success and .message for the error text on failure. Each response also carries an X-Validation header (a short-lived JWT whose jti matches the body); verifying it is optional.
The order module catalogue and shopping-cart endpoints are public (no X-API-Key) and return RAW JSON, not the envelope. GET /api/v1/products returns {"products": […]} directly, without success, jti or data.
Endpoints
The paths below omit the https://ljpc-hosting.nl prefix in the card titles but show it in the examples. Domain path parameters accept dots.
Domains and TLDs
The TLD catalogue, availability and price checks, and full domain management: register, transfer, update and fetch auth codes.
GET/api/hosting/v1/tlds
Returns the full sellable TLD catalogue with indicative catalogue list prices (EUR) per year. price is null when the catalogue price is unavailable; transfer_supported indicates whether the TLD supports an auth-code transfer. For the exact price of a specific domain including your valuepack, use /domain-names/price.
Checks the availability of a domain name. The answer is a string keyed per queried domain (for example available). A TLD we do not sell returns unknown.
Required permission:Check domain availability
Parameter
Type
Required
Description
domain
query string
Yes
The domain name to check. An array is also accepted.
Returns the exact price of a domain name for your account, including your valuepack, as a nested money object per domain, plus a premium flag. An unknown TLD returns null for that key.
Required permission:Check domain price
Parameter
Type
Required
Description
domain
query string
Yes
The domain name to price. An array is also accepted.
Detail of a domain. Live info from our system is merged over the stored row (falling back to the row when our system is temporarily unavailable). status may be a comma-joined status; treat it as opaque. A foreign domain returns 404.
Registers a new domain name on an owned WHOIS contact. The guard order rejects an unsupported TLD, a duplicate, or an unknown/foreign handle before the domain is registered in our system.
Required permission:Register domains through the API
201 Created
{ "success": true, "jti": "…", "data": { "handle": "<new-handle>" } }
422 { "success": false, "jti": "…",
"message": "The country field must be 2 characters." }
PATCH/api/hosting/v1/contacts/{handle}
Updates a WHOIS contact. Ownership first (404), then validation, then the update in our system. All fields are optional; unset fields keep their stored value.
Required permission:Update WHOIS contacts through the API
Manage the zone records of an owned domain. A record has the shape {type, name, content, ttl, prio}. Managed types are A, AAAA, CNAME, MX, TXT, SRV and CAA. NS records are not managed: they are excluded from GET output and preserved on PUT. name is relative to the zone (@ is the apex), ttl 60..86400, prio required for MX and SRV, otherwise null.
GET/api/hosting/v1/dns/zones/{domain}/records
Fetches the DNS records of an owned domain. 404 when the domain has no zone yet (treat that as an empty set).
Required permission:Manage DNS through the API
Parameter
Type
Required
Description
domain
path
Yes
The owned domain.
Example response
200 OK
{ "success": true, "jti": "…", "data": {
"zone": { "domain": "example.nl", "source": "manual" },
"records": [
{ "type": "A", "name": "@", "content": "192.0.2.10", "ttl": 3600, "prio": null }
] } }
404 { "success": false, "jti": "…", "message": "No DNS zone exists for this domain" }
PUT/api/hosting/v1/dns/zones/{domain}/records
Full replace of the managed record types. The first write creates a source=manual zone. An empty array is a valid clear. A zone managed by our hosting control panel returns 409. synced reports whether the change reached our DNS; false is normal until the domain delegates to our nameservers.
Required permission:Manage DNS through the API
Parameter
Type
Required
Description
domain
path
Yes
The owned domain.
records
body array
Yes
The complete desired record set, wrapped in a records key.
Detail of a site including disk usage. disk_quota_bytes is binary GiB (disk_space_gb × 1024³), null when unlimited. disk_used_bytes and email_used_bytes are null when no usage row exists yet. A foreign or unknown id returns 404 (no enumeration).
Required permission:Show own sites through the API
Returns a single-use SSO URL to the site control panel. 204 when no URL is available. Owner-scoped: you can only log in to owned or shared sites.
Required permission:Login to sites
Parameter
Type
Required
Description
id
path int
Yes
The site id.
Example response
200 OK
{ "success": true, "jti": "…", "data": {
"url": "https://panel.ljpc.systems/sso?token=<single-use-token>" } }
204 No Content
403 { "success": false, "jti": "…",
"message": "You don't have permission to login to this site" }
Orders
The product catalogue and placing and tracking orders. An order is created pending and delivered asynchronously; there are no webhooks, so poll GET /api/v1/orders/{id}.
GET/api/v1/products
Product catalogue. RAW JSON, no envelope. Without a key you get the list prices; when you send your X-API-Key, the prices are the price your account pays us per hosting product, including your valuepack. Optional ?category= filter. Prices are decimal strings; monthly_price is null for yearly-only products. Fulfillable hosting packages have a category_identifier of the form hosting_<N>gb, hosting_forward or hosting_dns.
Required permission:None (public, no key)
Parameter
Type
Required
Description
category
query string
No
Filter by category, for example hosting or domains.
Places an order through the restricted path. That path forces status=pending, source=reseller-api, client_id=owner_id=handler_id=the caller, send_invoice=false, send_quote=false, auto_deliver=true, keeps only meta.external_reference and rebuilds each cart_item from a whitelist with server-side pricing. The order id lives at data.order.id.
Required permission:Create orders
Parameter
Type
Required
Description
summary
body string
No
Short description of the order.
meta.external_reference
body string
No
Your own reference (the only meta kept).
order_lines
body array
Yes
Lines of {description, cart_item}. See the cart_item shape below this table.
Status detail of an order, including per-line provisioning. simple_status (and provisioning.overall) is pending, processing, completed or failed. The per-line state is pending, done or failed. Once a hosting line completes, site_id is filled in; feed it to GET /api/hosting/v1/sites/{id}. A foreign order returns 403, an unknown one 404.
Required permission:View own orders
Parameter
Type
Required
Description
id
path int
Yes
The order id (data.order.id from the create response).
The cart_item of an order line has a per-type shape: hosting = {"type":"hosting","product_uuid":"…","domain":"…","billing_period":"monthly|yearly"} (billing_period only needed for client_choice products; target_url required for hosting_forward); domain = {"type":"domain","action":"register|transfer","sld":"…","tld":"…","auth_code":"…"} (auth_code required for transfer); own space = {"type":"hosting_own","space_id":<int>,"domain":"…"} (price 0).
Flows
The main reseller flows, step by step.
Register a domain
1Create a WHOIS contact (POST /contacts) or reuse an existing handle (GET /contacts).
2Check availability and price (GET /domain-names/check and /price).
3Register the domain (POST /domain-names) with domain and contact_handle.
4Afterwards manage nameservers and DNS (PATCH /domain-names/{domain}, PUT /dns/zones/{domain}/records).
Order hosting and provision
1Look up the product (GET /api/v1/products) and pick the package product_uuid.
2Place the order (POST /api/v1/orders) with a hosting cart_item.
3Poll GET /api/v1/orders/{id} until simple_status is completed or failed (no webhooks).
4Read the hosting line site_id from provisioning.lines.
5Use the site: GET /sites/{id} for usage, GET /sites/{id}/login for SSO.
Manage DNS (full replace)
1Read the current set (GET /dns/zones/{domain}/records). 404 means no zone yet; treat it as empty.
2Build the complete desired record set (managed types only; NS is preserved automatically).
3Replace everything with PUT /dns/zones/{domain}/records and a {"records":[…]} body (an empty array clears it).
4Check synced in the response (informational; stays false until the domain delegates to our nameservers).
Asynchronous provisioning
Provisioning is asynchronous and there are no webhooks. Poll GET /api/v1/orders/{id} until simple_status is completed or failed, then read the site via the line site_id. In production a queue worker delivers the orders.
Deliver-first billing
The restricted order path sets auto_deliver=true and send_invoice=false: the service is delivered first and billed afterwards through a subscription (needs_subscription and needs_payment in the response point to that follow-up). You pay your account price including your valuepack; you set your own retail price.
Caveats
Login is owner-scoped
SSO only logs in to your own or shared sites.
Unknown TLD returns "unknown"
For a TLD we do not sell, /check returns unknown and /price returns null. Treat that as not sellable.
Deliver-first subscription billing
Orders are delivered without sending an invoice or quote; billing then runs through a subscription.
Prices include your valuepack
All prices from the API are the price your account pays us, including your valuepack (excl. VAT). On top of that, as a reseller you set the retail price for your client yourself.
Reference implementation
Hosting panel boilerplate
A complete example integration against this API: domains, DNS, ordering and provisioning hosting.
Stay up to date with recent developments!Subscribe and receive our newsletterSigning up...Thank you for subscribing!Something went wrong. Please try again later.