L
lime.dev
Skip to Content
API ReferenceAPI Overview

API Overview

Reference for the Lime ENS offchain resolver gateway: CCIP-Read resolution, subdomain reservation, records, eligibility checks, and account endpoints.

Base URL: http://localhost:8080 (default port; use your deployed gateway URL in production)

Content-Type: application/json

CORS: Enabled for all origins (*)


Endpoints

1. Health Check

Check server status and signer configuration.

GET /health

Response (200 OK):

{ "status": "ok", "signer": "0x...", "timestamp": 1706544000, "indexer": { "enabled": true, "synced": true, "last_block": 12345678, "current_block": 12345679, "behind": 1 } }

When the onchain indexer is disabled (no ETH_RPC_URL or RESOLVER_ADDRESS), the indexer object is omitted. When the indexer is enabled but RPC fails, indexer includes "synced": false and "error": "..." instead of current_block / behind.

FieldTypeDescription
statusstring"ok" or "error"
signerstringBackend signer address (hex)
timestampnumberUnix timestamp
indexerobjectOptional. Onchain indexer sync state: enabled, synced, last_block, current_block, behind; or error when RPC fails. Omitted when indexer is disabled.

2. CCIP-Read (ENS Resolution)

EIP-3668 CCIP-Read endpoint for offchain ENS resolution. Called by the HybridResolver contract.

POST /

Request body:

{ "data": "0x...", "extraData": "0x...", "sender": "0x..." }
FieldTypeRequiredDescription
datastringYesHex-encoded callData (name + function selector)
extraDatastringNoHex-encoded (callData, targetAddress)
senderstringNoAlternative: sender address

Response (200 OK):

{ "data": "0x..." }

The data field contains ABI-encoded (bytes result, uint64 expires, bytes sig).


API v1 — Subdomain Reservation

3. Check Name Availability

Check if a subdomain is available and whether the wallet can register it. No authentication required.

GET /api/v1/names/check

Query parameters (one backend instance per network; no chainId):

ParameterTypeRequiredDescription
namestringYesENS name (e.g., test.free.eth)
addressstringNoWallet address for permission check

Example:

GET /api/v1/names/check?name=test.free.eth&address=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5

Response (200 OK):

{ "available": true, "can_register": true, "resolution": "offchain" }
FieldTypeDescription
availablebooleanWhether the name is available
can_registerboolean | nullWhether the wallet can register (null if wallet required)
reasonstringOptional: name_taken, wallet_required
resolutionstring"offchain" | "onchain" (reserved for future)

Response variants:

Scenarioavailablecan_registerreason
Name takenfalsename_taken
Free, parent has *truetrue
Free, parent restrictedtruenullwallet_required
Free, wallet can registertruetrue
Free, wallet cannottruefalse

Error (400 Bad Request):

{ "error": "parent domain not under backend control" }

4. Import Resolver by ENS Name

If the ENS resolver for this name is the backend “lime” resolver, the backend will import the name into its ens_records table using the ENS owner address. For a new import, the row is created with expires_at null (no backend-managed expiry; on-chain ENS controls registration). Renew in this API only applies to names that have a non-null backend expiry (subnames from Reserve). If the resolver is any other address, the backend returns an error containing the current resolver and resolved type.

GET /api/v1/names/resolver

Query parameters:

ParameterTypeRequiredDescription
namestringYesENS name (e.g., lime.eth)

Example:

GET /api/v1/names/resolver?name=lime.eth

Response (200 OK, only when resolver type is lime):

{ "record": { "name": "lime.eth", "node": "0x...", "address": "0x...", "expires_at": null }, "owner": "0x...", "resolver": "0x4255f8BC8E92729abe8d4343d6d656D4C69Dd564", "type": "lime", "already_imported": false }

When the name’s node already exists in backend ens_records before this request (case 3: lime resolver, row already present), the import still succeeds (200 OK). The backend refreshes owner_address and the default address to the current ENS owner, replaces manager rights for that node with the ENS owner, and upserts multichain ETH (60) as for a new import.

  • already_imported is true
  • message explains that the name was already in lime and owner/managers were synced
FieldTypeDescription
recordobjectImported ens_records row (lightweight projection)
ownerstringENS owner address
resolverstringResolver address from ENS Registry
typestringResolver type: lime | standart | custom
already_importedbooleantrue if an ens_records row for this node existed before this call
messagestringPresent when already_imported is true: owner + managers aligned with ENS

Resolver type rules:

  • lime: resolver equals backend RESOLVER_ADDRESS
  • standart: resolver equals ENS standard resolver address
  • custom: any other resolver address

standart spelling is kept intentionally for API compatibility.

Errors:

  • 400: name query parameter is missing
  • 409: resolver is not lime (includes current resolver and type)
  • 502: failed to read resolver/owner from Ethereum RPC
  • 503: Ethereum RPC is not configured (ETH_RPC_URL is empty)

5. Reserve Name

Register a subdomain. Requires EIP-712 signature from the owner.

POST /api/v1/names/reserve

Request body:

{ "name": "test.free.eth", "owner": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5", "chainId": 11155111, "signature": "0x...", "domain": { "name": "ENS Offchain Resolver", "version": "1", "chainId": 11155111, "verifyingContract": "0x..." }, "timestamp": 1706544000 }
FieldTypeRequiredDescription
namestringYesENS name to register
ownerstringYesOwner wallet address (hex)
chainIdnumberNoNetwork ID (default: 1)
signaturestringYesEIP-712 signature (65 bytes, hex)
domainobjectYesEIP-712 domain separator
timestampnumberYesUnix timestamp (replay protection, 15 min window)

EIP-712 Typed Data (ReserveName):

  • Domain: name, version, chainId, verifyingContract
  • Message: ReserveName(string name, address owner, uint64 timestamp, uint256 chainId)

Response (201 Created):

{ "status": "ok", "name": "test.free.eth" }

Error (400 Bad Request):

{ "error": "name is taken" }
{ "error": "wallet does not have permission to register this name" }
{ "error": "signature expired or invalid timestamp" }

6. Renew Name

Sets the expiration for a registered name (same rule as reserve: now + parent's subdomain_registration_duration_days, not an extension on top of the previous expiry). Signer must be the owner or a manager of the parent domain. Uses EIP-712 typed data RenewName.

POST /api/v1/names/renew

Request body (same shape as Reserve):

{ "name": "test.free.eth", "owner": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5", "chainId": 11155111, "signature": "0x...", "domain": { "name": "ENS Offchain Resolver", "version": "1", "chainId": 11155111, "verifyingContract": "0x..." }, "timestamp": 1706544000 }
FieldTypeRequiredDescription
namestringYesENS name to renew
ownerstringYesOwner wallet address (hex)
chainIdnumberNoNetwork ID (default: 1)
signaturestringYesEIP-712 signature for RenewName (65 bytes, hex)
domainobjectYesEIP-712 domain separator
timestampnumberYesUnix timestamp (replay protection, 15 min window)

EIP-712 Typed Data (RenewName):

  • Domain: name, version, chainId, verifyingContract
  • Message: RenewName(string name, address owner, uint64 timestamp, uint256 chainId)

Response (200 OK):

{ "status": "ok", "name": "test.free.eth" }

Errors (400 Bad Request):

  • record not found — name does not exist or is expired (expired names can still be renewed)
  • name cannot be renewed: no backend-managed expiry (imported or root name)expires_at is null (imported ENS name, root, or any record without backend expiry)
  • wallet is not owner or manager of this name — signer is not owner of the name nor manager of the parent
  • signature expired or invalid timestamp / signature does not match owner

7. Get All Records (Profile / Display)

Returns all ENS records for a name. Intended for profile/display pages. Missing values are returned as null (aligned with ENS API and frontend best practices).

GET /api/v1/names/records

Query parameters:

ParameterTypeRequiredDescription
namestringYesENS name (e.g., test.free.eth)

Example:

GET /api/v1/names/records?name=test.free.eth

Response (200 OK):

{ "name": "test.free.eth", "node": "0x...", "address": "0x284cc50eb6C93c55BA4963D0dC5097C54db71580", "addresses": { "0": { "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "onchain": false }, "60": { "address": "0x284cc50eb6C93c55BA4963D0dC5097C54db71580", "onchain": true }, "501": { "address": "HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH", "onchain": false }, "614": { "address": "0x284cc50eb6C93c55BA4963D0dC5097C54db71580", "onchain": false } }, "content_hash": null, "pubkey": null, "text": { "url": "https://example.com", "avatar": null, "description": null, "notice": null, "keywords": null, "com.twitter": null, "com.github": null, "email": null, "location": null, "phone": null, "name": null }, "abi": [ { "type": "function", "name": "transfer", "inputs": [{ "name": "to", "type": "address" }, { "name": "amount", "type": "uint256" }], "outputs": [], "stateMutability": "nonpayable" } ], "zonehash": null, "reverse_name": null, "managers": [], "eligibility_url": null, "expires_at": "2026-01-29T12:00:00Z", "resolution": "hybrid" }
FieldTypeDescription
namestringENS name
nodestringNamehash as hex (0x…)
addressstring | nullDefault ETH address (hex)
addressesobjectcoinType (string) → { "address": string, "onchain": boolean }. address is the value (hex, bech32, base58, etc.); onchain is true if from onchain setAddr (HybridResolver), false if offchain (DB). Common keys: 0 = Bitcoin, 60 = Ethereum, 501 = Solana, 614 = Optimism. Empty {} if none.
content_hashstring | nullDecoded URI: ipfs://<cid>, ipns://<cid>, or 0x...; null if not set. Resolver uses raw EIP-1577 bytes from storage.
pubkeyobject | null{ "x": "0x...", "y": "0x..." } (hex); null if not set or zero
textobjectStandard ENS text keys + custom keys; value or null for each key
abiarrayABI as flat array of fragments. Empty [] if none. No content_types/offset wrapper.
zonehashstring | nullZone hash (hex); null if not set
reverse_namestring | nullReverse resolution name; null if not set
expires_atstring | nullExpiration (ISO8601); null if never expires
resolutionstring"offchain" | "hybrid" | "onchain" — classification of merged address sources (same rules as Get Account Names): hybrid if some coin types use onchain setAddr and others use offchain DB; onchain if the name is primary (exactly two labels, e.g. foo.eth or lime.dev) and every present address is onchain; offchain if the name is a subname (three or more labels) and no onchain addresses. A primary name with only offchain addresses is hybrid. Single-label names fall back to hybrid.
managersarrayManager addresses for this name (who can manage it); may include "*" for open access. Empty [] if none. Each name has its own list; changes via POST /api/v1/names/manage do not affect other sub-names.
eligibility_urlstring | nullExternal API URL used for subdomain eligibility checks for this name’s node (name_registration_config). null if not configured. Same value you set with POST /api/v1/names/manage.

Error (400 Bad Request):

{ "error": "name is required" }

Error (404 Not Found):

{ "error": "record not found" }

7.1 Update Records (POST)

Update ENS record fields for a name. The signer must be the owner or manager of the name. Only fields present in the body are updated; omitted fields are left unchanged. Use null to clear a field (e.g. remove a text key). Replay protection: timestamp must be within 15 minutes. To change manager addresses or eligibility URL for the name, use POST /api/v1/names/manage (owner only; see 7.2 below).

POST /api/v1/names/records

Request body:

FieldTypeRequiredDescription
namestringYesENS name to update (e.g. test.free.eth)
chainIdnumberYesNetwork ID (1 = Mainnet, 11155111 = Sepolia)
timestampnumberYesUnix timestamp (replay protection; valid 15 min window)
domainobjectYesEIP-712 domain separator (name, version, chainId, verifyingContract)
signaturestringYesEIP-712 signature (65 bytes hex) of UpdateRecords(name, chainId, timestamp, payloadHash)
addressstring | nullNoDefault ETH address (hex); omit = no change, null = clear
addressesobjectNocoinType (string) → address: 0 = BTC, 60 = ETH, 501 = Solana, 614 = Optimism; omit = no change; empty string = delete that coin type
content_hashstring | nullNoContent hash: hex 0x... (EIP-1577 bytes), or ipfs://<cid>, or ipns://<cid>; omit = no change, null or "" = clear
pubkeyobject | nullNo{ "x": "0x...", "y": "0x..." } (32 bytes each); omit = no change, null = clear
textobjectNokey → value; omit = no change; key with null = delete that key
abiarrayNoABI: ENSIP-4 [{ "content_types": "0x...", "offset": "0x...", "abi": [...] }] or flat [{ "type": "function", "name": "foo", "inputs": [...] }, ...] (stored as one record with content_types=1 = JSON per ENSIP-4, offset=0; so resolver ABI(node, 1) returns it); omit = no change; empty [] = clear all
zonehashstring | nullNoZone hash (hex); omit = no change, null = clear
reverse_namestring | nullNoReverse name; omit = no change, null or "" = clear

EIP-712 Typed Data (UpdateRecords):

  • Domain: same as Reserve (name, version, chainId, verifyingContract).
  • Message: UpdateRecords(string name, uint256 chainId, uint64 timestamp, bytes32 payloadHash).
  • payloadHash: keccak256(canonicalJSON(payload)) where payload is the JSON object containing only the record fields being updated (address, addresses, content_hash, pubkey, text, abi, zonehash, reverse_name). Keys must be sorted alphabetically for canonical encoding.

Example request (set default address, multichain addresses for BTC / Solana / Optimism, and text):

{ "name": "test.free.eth", "chainId": 11155111, "timestamp": 1706544000, "domain": { "name": "ENS Offchain Resolver", "version": "1", "chainId": 11155111, "verifyingContract": "0x..." }, "signature": "0x...", "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5", "addresses": { "0": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "60": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5", "501": "HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH", "614": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5" }, "text": { "url": "https://example.com", "avatar": null } }

Coin types (SLIP-44) for addresses:

coinTypeChainAddress format example
0Bitcoinbc1q... (SegWit/bech32) or bc1p... (Taproot/bech32m)
60Ethereum0x... (20 bytes hex)
501SolanaBase58 (e.g. HN7cABqLq46...)
614Optimism0x... (same EVM format as Ethereum)

Response (200 OK):

{ "status": "ok", "name": "test.free.eth" }

Errors (400 Bad Request):

{ "error": "name and signature are required" }
{ "error": "signature expired or invalid timestamp" }
{ "error": "wallet is not owner or manager of this name" }

Error (404 Not Found):

{ "error": "record not found" }

7.2 Manage name settings (POST)

Update managers and/or eligibility_url for a name. The signer must be the owner of the name (wallet matching address or owner_address on the record); delegated managers cannot call this endpoint. Replay protection: timestamp must be within 15 minutes.

POST /api/v1/names/manage

Request body:

FieldTypeRequiredDescription
namestringYesENS name (e.g. test.free.eth)
chainIdnumberYesNetwork ID (1 = Mainnet, 11155111 = Sepolia)
timestampnumberYesUnix timestamp (replay protection; valid 15 min window)
domainobjectYesEIP-712 domain separator (name, version, chainId, verifyingContract)
signaturestringYesEIP-712 signature (65 bytes hex) of ManageName(name, chainId, timestamp, payloadHash)
managersarrayNoFull replacement list of manager addresses; may include "*" for open access. Omit = no change. Does not affect other sub-names.
eligibility_urlstring | nullNoURL used for subdomain eligibility checks. Omit = no change; null or "" clears the stored URL.

EIP-712 Typed Data (ManageName):

  • Domain: same as Reserve / Update Records (name, version, chainId, verifyingContract).
  • Message: ManageName(string name, uint256 chainId, uint64 timestamp, bytes32 payloadHash).
  • payloadHash: keccak256(canonicalJSON(payload)) where payload contains only the management fields present in the body (eligibility_url, managers). Keys must be sorted alphabetically for canonical encoding. If neither field is sent, payload is the empty object {}.

Response (200 OK):

{ "status": "ok", "name": "test.free.eth" }

Errors (400 Bad Request):

  • name and signature are required / invalid JSON / signature expired or invalid timestamp / invalid signature
  • only the owner can manage name settings
  • invalid manager address: ... (must be 0x... or *)
  • invalid eligibility_url: ...

Error (404 Not Found):

{ "error": "record not found" }

8. Get Account Names

Returns all ENS names where the wallet address is owner or manager (for “My names” / account page). Each owned name includes its registration end date (expires_at) and a resolution field describing how address records for that name are sourced (merged view matches GET /api/v1/names/records addresses / onchain flags). Non-address data (text, content hash, ABI, etc.) is always stored offchain in this backend and does not change resolution.

resolution values

ValueMeaning
offchainThe name is a subname (three or more dot-separated labels, e.g. a.b.eth, padel.lime.dev) and every present address is offchain (DB only; no onchain setAddr for any coin type).
hybridMixed sources: at least one coin type is onchain and at least one is offchain — or any case that is neither pure offchain nor pure onchain below (e.g. a primary two-label name with only offchain addresses, or a single-label name).
onchainThe name is primary (exactly two labels, e.g. foo.eth or lime.dev) and every present address is onchain (no offchain-only coin types in the merged set).
GET /api/v1/account/names

Query parameters:

ParameterTypeRequiredDescription
addressstringYesWallet address (hex, with or without 0x)
chainIdnumberNoReserved for future use (one backend instance per network today).

Example:

GET /api/v1/account/names?address=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5

Response (200 OK):

{ "owned": [ { "name": "test.free.eth", "expires_at": "2026-01-29T12:00:00Z", "resolution": "hybrid" }, { "name": "log01.eth", "expires_at": null, "resolution": "hybrid" } ], "managed": ["free.eth"] }
FieldTypeDescription
ownedobject[]Names where the address is owner; each has name, expires_at (ISO8601 or null), and resolution ("offchain" | "hybrid" | "onchain"). Expired names are excluded.
managedstring[]Parent domain names where the address is manager (can register subdomains). Names the address already owns are excluded.

Error (400 Bad Request):

{ "error": "address is required" }
{ "error": "invalid address length" }

9. Get Statistics

Returns aggregate statistics (names claimed, total record fields). Extensible for future metrics.

GET /api/v1/statistics

Response (200 OK):

{ "names_claimed": 42, "records_count": 156 }
FieldTypeDescription
names_claimednumberTotal number of claimed names (rows in ens_records).
records_countnumberTotal non-null record fields: multichain addresses, text records, content hash, pubkey, zonehash, reverse name, ABI, DNS, and default address in ens_records.

10. Get setAddr Signature (Onchain Resolver)

Returns all data needed to form a setAddr(node, coinType, addressBytes, deadline, nonce, signature) transaction on the onchain resolver (HybridResolver / OnchainAddrResolver). By default addressBytes are taken from offchain data (ENS records / multichain addresses). With remove=true, addressBytes is the zero address (0x0000..., 20 bytes) to clear/remove the onchain record. The client never passes nonce. The backend obtains the current nonce from the indexer (contract’s NonceIncremented event); for a node that has not had setAddr yet, the backend uses nonce 0. The response always includes nonce so the client can call resolver.setAddr(..., nonce, signature) with the returned value. Signature is valid for 12 minutes (backend window); the contract also enforces a maximum deadline TTL of 7 days.

GET /api/v1/signature/address?name=lime.eth&coinType=60 GET /api/v1/signature/address?name=lime.eth&coinType=60&remove=true POST /api/v1/signature/address

Query parameters (GET):

ParameterTypeRequiredDescription
namestringYesENS name (e.g. lime.eth)
coinTypenumberYesSLIP-44 coin type (e.g. 60 for ETH)
removebooleanNoIf true or 1, returns addressBytes = 0x0 (20 zero bytes) to remove the onchain record

Request body (POST):

{ "name": "lime.eth", "coinType": 60, "remove": false }
FieldTypeRequiredDescription
namestringYesENS name
coinTypenumberYesSLIP-44 coin type
removebooleanNoIf true, returns addressBytes = 0x0 to clear the onchain record

Response (200 OK):

{ "node": "0x...", "coinType": 60, "addressBytes": "0x...", "deadline": 1706544720, "nonce": 0, "signature": "0x..." }
FieldTypeDescription
nodestringNamehash as hex (bytes32)
coinTypenumberCoin type (uint256)
addressBytesstringAddress bytes (hex). Zero address 0x0000... when remove=true
deadlinenumberUnix timestamp; signature valid for 12 minutes (backend window)
noncenumberNonce used in the signature; must equal contract’s nonces[node] when calling setAddr
signaturestringBackend signature (r+s+v, 65 bytes hex) for the onchain resolver

The client does not pass nonce; the backend provides it in the response (from the indexer or 0 for a new node). Call resolver.setAddr(node, coinType, addressBytes, deadline, nonce, signature) with the returned values. Use remove=true to get a signature for clearing the onchain address (addressBytes = 0x0).

Errors (400 Bad Request):

  • name is required / coinType is required
  • stored nonce unavailable — backend could not read nonce from the indexer DB
  • no address found for name "..." and coinType N — no offchain record for this name and coin type (only when remove is not set)

11. Verify External Eligibility

Calls an external API to check whether a wallet address is eligible (e.g., whitelisted, meets a condition). The backend makes a GET request to the provided URL with the wallet_address query parameter and expects the external API to return JSON {"result": true} or {"result": false}.

Security: The endpoint validates both the URL (blocks private/loopback IPs, restricts to http/https schemes, prevents SSRF via DNS resolution check) and the wallet address (must be 0x + 40 hex characters).

POST /api/v1/eligibility/verify

Request body:

{ "api_url": "https://example.com/api/check-eligibility", "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5" }
FieldTypeRequiredDescription
api_urlstringYesExternal API URL (http/https only; no private/loopback IPs)
wallet_addressstringYesWallet address (0x + 40 hex characters)

External API contract: The backend appends ?wallet_address=0x... to api_url (existing query parameters are preserved) and sends a GET request. The external API must return a 2xx status and JSON body:

{ "result": true }
FieldTypeDescription
resultbooleantrue if eligible, false otherwise

Response (200 OK):

{ "eligible": true, "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5", "api_url": "https://example.com/api/check-eligibility" }
FieldTypeDescription
eligiblebooleanWhether the wallet passed the external check
wallet_addressstringThe wallet address that was verified
api_urlstringThe external API URL that was called

Errors (400 Bad Request):

{ "error": "api_url and wallet_address are required" }
{ "error": "invalid wallet address format: must be 0x followed by 40 hex characters" }
{ "error": "invalid api_url: unsupported scheme \"ftp\": only http and https are allowed" }
{ "error": "invalid api_url: requests to private/reserved addresses are not allowed" }
{ "error": "external API returned status 404" }

Security measures:

  • URL scheme restricted to http and https
  • Blocks localhost, 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, link-local, and unspecified addresses
  • Post-DNS-resolution IP check prevents SSRF via DNS rebinding
  • Credentials in URL (user:pass@host) are rejected
  • Response body limited to 1 MB
  • HTTP timeout: 10 seconds; max 3 redirects

EIP-712 Signing (Reserve / Renew)

The client must sign the typed data with the owner’s private key. For Reserve use type ReserveName; for Renew use type RenewName with the same message shape (name, owner, timestamp, chainId).

Example (ethers.js v6):

const domain = { name: "ENS Offchain Resolver", version: "1", chainId: 11155111, verifyingContract: "0x..." }; const types = { ReserveName: [ { name: "name", type: "string" }, { name: "owner", type: "address" }, { name: "timestamp", type: "uint64" }, { name: "chainId", type: "uint256" } ] }; const value = { name: "test.free.eth", owner: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5", timestamp: Math.floor(Date.now() / 1000), chainId: 11155111 }; const signature = await signer.signTypedData(domain, types, value);

Chain IDs

NetworkchainId
Ethereum Mainnet1
Sepolia11155111

Error Format

All errors return JSON:

{ "error": "Error message" }

HTTP status codes:

  • 200 — Success
  • 201 — Created (reserve); 200 — OK (renew)
  • 400 — Bad Request (invalid params, validation failed)
  • 404 — Not Found (records: name not found or expired)
  • 405 — Method Not Allowed
  • 503 — Service Unavailable (health check when signer is nil)

MIT 2026 © Lime.dev. Free ENS subnames, no gas, no fees.