Skip to content

Packages and usage

Every account has its own package for each of Dinio’s two services — a line:

Line Service What its package limits
auth DNS hosting Zones, free-domain leases, health checks, how far back zone statistics go.
filter DNS filtering Client identities, sinkhole lists and rules, how far back client statistics go.

The two are independent: you can have a package in one line, both, or neither. Without a package in a line, nothing can be added in it — every create there is refused with 403 NO_SUBSCRIPTION. Choose one with Choose a package.

A limit only stops you from adding. Nothing you already have is ever removed, suspended or stops answering because of a package.

Each limit has a key. A number is the limit; null means unlimited.

Key Line Limit
auth.zones.max auth Zones you own. Leases of free domains don’t count here.
auth.free_subdomains.max auth Free-domain leases you own.
auth.healthchecks.max auth Health checks in the zones you own.
auth.healthchecks.min_interval_s auth Shortest health-check interval allowed, in seconds (at least 30).
auth.logs.retention_days auth How many days back zone statistics reach; 0 = none.
filter.clients.max filter Client identities you own.
filter.sinkhole_lists.max filter Sinkhole lists you own.
filter.sinkhole_rules.max filter Rules in all of your lists together.
filter.sinkhole_rules_per_list.max filter Rules in any one of your lists.
filter.logs.retention_hours filter How many hours back client statistics reach.
filter.queries.monthly filter Lookups by your clients per month.

A package may leave out the three filter.sinkhole_* keys; then that limit doesn’t apply. New keys can appear — treat an unknown key as information to show, not an error.

Whose package counts: the owner’s, not the caller’s. Everything in a zone counts against the zone’s Owner — a health check an Administrator adds, too. Identities and lists count against the user who owns them.

Where the limits are checked:

Request Keys
POST /zones auth.zones.max; for a lease auth.free_subdomains.max
POST /health-checks auth.healthchecks.max, auth.healthchecks.min_interval_s
PATCH /health-checks/:record_id with a new interval auth.healthchecks.min_interval_s
Zone import with health checks auth.healthchecks.max, auth.healthchecks.min_interval_s
POST /client-identities filter.clients.max
POST /sinkhole-lists filter.sinkhole_lists.max
POST /sinkhole-roles, list import and URL sources filter.sinkhole_rules.max, filter.sinkhole_rules_per_list.max
/stats/zones/… auth.logs.retention_days
/stats/clients/… filter.logs.retention_hours
GET /api/v1/plans

The price list: every listed package, in both lines. Needs no API key, so a pricing page can use it before sign-up. The response may be cached for five minutes.

Terminal window
curl https://api-dinio.bitelex.com/api/v1/plans
{
"success": true,
"data": [
{
"id": "auth-pro@v1",
"line": "auth",
"code": "pro",
"version": 1,
"name": "Pro",
"self_service": true,
"prices": [
{ "period": "monthly", "currency": "EUR", "amount_cents": 900, "tax_inclusive": false },
{ "period": "yearly", "currency": "EUR", "amount_cents": 9000, "tax_inclusive": false }
],
"entitlements": {
"auth.zones.max": 20,
"auth.free_subdomains.max": 3,
"auth.healthchecks.max": 50,
"auth.healthchecks.min_interval_s": 30,
"auth.logs.retention_days": 30
}
}
]
}

(The values are an example; read the real ones from the API.)

Field
id The exact version: <line>-<code>@v<version>.
code What you choose by, e.g. pro.
self_service false: not for self-service — show “contact us” instead of a choose button.
prices One per billing period, in cents, without VAT (tax_inclusive: false). Empty = free.
entitlements The package’s limits; null = unlimited.

Packages come in their display order, smallest first within a line.

GET /api/v1/account/subscriptions

Your package per line — none, one or two items. Needs a personal key; a zone key has no account and gets 403.

interface Subscription {
uuid: string;
user_id: string;
line: 'auth' | 'filter';
plan_id: string; // the exact version, e.g. "auth-pro@v1"
status: 'trial' | 'active' | 'past_due' | 'canceled';
period: 'monthly' | 'yearly' | null; // null for a free package
trial_ends_at?: number;
current_period_end?: number;
created_at: number;
updated_at: number;
}

You keep the version you chose: when a package gets a new version, your limits don’t change until you choose again.

PUT /api/v1/account/subscriptions/:line

Puts you on a package in :line (auth or filter), in its current version. Returns the new Subscription.

Field Type
code string The package’s code from GET /plans.
period string monthly or yearly. Required for a paid package, refused for a free one.
Terminal window
curl -X PUT -H "Authorization: Bearer $DINIO_API_KEY" -H "Content-Type: application/json" \
-d '{"code":"pro","period":"yearly"}' \
https://api-dinio.bitelex.com/api/v1/account/subscriptions/auth

A free package starts active, a paid one trial. Choosing the package you already have with another period keeps your version.

  • 409 PLAN_NOT_AVAILABLE — no such package, or it is not for self-service.
  • 409 PLAN_DOWNGRADE_BLOCKED — you use more than the package allows. Nothing changes; details.violations lists every limit you are over, so you can show what to remove first.
{
"success": false,
"error": {
"code": "PLAN_DOWNGRADE_BLOCKED",
"message": "the package doesn't fit what is in use: auth.zones.max, auth.healthchecks.min_interval_s",
"details": {
"line": "auth",
"violations": [
{ "key": "auth.zones.max", "limit": 3, "used": 7 },
{ "key": "auth.healthchecks.min_interval_s", "limit": 60, "used": 30, "count": 4 }
]
}
}
}

For a count, used is how many you have. For auth.healthchecks.min_interval_s, used is your fastest interval and count how many checks run faster than limit. How far back statistics reach never blocks a change.

GET /api/v1/account/usage

Per line: your package, its effective limits, and how much of each you use.

Terminal window
curl -H "Authorization: Bearer $DINIO_API_KEY" https://api-dinio.bitelex.com/api/v1/account/usage
{
"success": true,
"data": [
{
"line": "auth",
"subscription": { "plan_id": "auth-pro@v1", "status": "active", "period": "yearly", "…": "…" },
"plan": { "id": "auth-pro@v1", "code": "pro", "version": 1, "name": "Pro" },
"entitlements": {
"auth.zones.max": { "limit": 20, "used": 17, "level": "warn" },
"auth.logs.retention_days": { "limit": 30 }
}
},
{ "line": "filter", "subscription": null, "plan": null, "entitlements": null }
]
}

limit can differ from the price list when your account has an individual arrangement. Counted limits also have used and level:

level
ok Below 80 %.
warn 80 % or more.
limit At the limit: the next add is refused.
over Above it — possible after a package change by support. Everything keeps working; only adding is refused.

A line without a package has plan and entitlements set to null.

Over a limit, the request is refused with 403 PLAN_LIMIT and nothing is written. details names the limit:

{
"success": false,
"error": {
"code": "PLAN_LIMIT",
"message": "plan limit reached: auth.zones.max (20 of 20)",
"details": { "key": "auth.zones.max", "limit": 20, "used": 20 }
}
}

For a count, details has used; for a setting — a health-check interval, how far back statistics reach — it has requested instead. To offer an upgrade, take the next package of that line in GET /plans whose value for details.key is higher (or null); for auth.healthchecks.min_interval_s, lower.

Without a package in the line, the error is 403 NO_SUBSCRIPTION with details.line (auth or filter).

A create that succeeds but brings a limit to 80 % or more carries warnings next to data:

{
"success": true,
"data": { "uuid": "…", "name": "example.com", "…": "…" },
"warnings": [
{
"code": "PLAN_USAGE",
"message": "auth.zones.max: 17 of 20 used",
"details": { "key": "auth.zones.max", "limit": 20, "used": 17, "level": "warn" }
}
]
}

level is warn or limit. Show it to the user, with the offer of a bigger package. Responses without warnings leave the field out.