Documentation

Guides and the API

API reference

Scoped keys · REST · examples use placeholder data

Authentication

Create a key in Dashboard → API keys. Keys look like tsc_…, are shown exactly once, and carry only the scopes you pick. Send the key as a bearer token:

curl https://tsoftcloud.com/api/v1/services \
  -H "Authorization: Bearer tsc_your_key_here"

401 means a missing or revoked key; 403 names the scope the key lacks.

Conventions

  • Money is always integer minor units as strings ("800000" = ₦8,000.00) with an explicit ISO currency — never floats.
  • Timestamps are ISO 8601 in UTC.
  • Responses wrap payloads in { "data": … }; errors in { "error": "…" }.
  • Rate limit: 120 requests/minute per key — exceeding it returns 429 with a Retry-After header.
  • Registrar-backed reads degrade honestly: "available": false plus a message, never a 500.

GET /api/v1/services services:read

{
  "data": [
    {
      "id": 12,
      "type": "email",
      "name": "Business email — verifytest.ng",
      "domain": "verifytest.ng",
      "status": "active",
      "cycle": "annually",
      "amount_minor": "1500000",
      "currency": "NGN",
      "next_due_date": null,
      "created_at": "2026-07-03T04:05:00.000Z"
    }
  ]
}

GET /api/v1/invoices invoices:read

{
  "data": [
    {
      "id": 34,
      "number": "INV-2026-0034",
      "status": "paid",
      "currency": "NGN",
      "subtotal_minor": "800000",
      "tax_minor": "0",
      "total_minor": "800000",
      "due_date": "2026-08-01T00:00:00.000Z",
      "paid_at": "2026-07-20T09:12:00.000Z",
      "created_at": "2026-07-18T10:00:00.000Z"
    }
  ]
}

GET /api/v1/domains domains:read

{
  "data": [
    {
      "id": 8,
      "domain": "yourbrand.com.ng",
      "status": "active",
      "next_due_date": "2027-07-01T00:00:00.000Z",
      "created_at": "2026-07-01T09:00:00.000Z"
    }
  ]
}

GET /api/v1/domains/{domain} domains:read

{
  "data": {
    "domain": "yourbrand.com.ng",
    "available": true,
    "registrar_status": "active",
    "expires_at": "2027-07-01T00:00:00.000Z",
    "locked": true,
    "whois_privacy": true,
    "nameservers": ["dns1.example.net", "dns2.example.net"]
  }
}

// When the registrar can't answer, "available": false arrives with a
// human-readable "message" instead of an error — degradation is honest.

GET · PUT /api/v1/domains/{domain}/dns domains:read · dns:write

// PUT replaces the ENTIRE zone — read, modify, then write back.
curl -X PUT https://tsoftcloud.com/api/v1/domains/yourbrand.com.ng/dns \
  -H "Authorization: Bearer tsc_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "records": [
      { "type": "A", "name": "@", "address": "203.0.113.10", "ttl": 1800 },
      { "type": "MX", "name": "@", "address": "mail.yourbrand.com.ng", "mxPriority": 10 }
    ]
  }'

// → { "data": { "domain": "yourbrand.com.ng", "records": 2, "applied": true } }

GET · PUT /api/v1/domains/{domain}/nameservers domains:read · dns:write

// PUT body: 2–5 hostnames. Custom nameservers override the DNS zone above.
{ "nameservers": ["ns1.yourdns.com", "ns2.yourdns.com"] }

// → { "data": { "domain": "…", "nameservers": [...], "applied": true } }

Need a higher limit?

The domains:read and dns:write scopes are live. Want a higher rate limit, or an endpoint we don't have yet?

Talk to us

Still stuck?

Message us from the contact form and we'll get back to you.