Skip to main content

License Verification API

License Verification API

When you sell software on 3DIMLI, each buyer receives a unique Order Item ID after purchase. This acts as their license key. Your software or license server can call 3DIMLI's verification API to confirm whether a key is legitimate and which license tier was purchased.

Who is this for?

This page is for developers listing software on 3DIMLI who want to add license activation and verification to their products. For a step-by-step integration walkthrough, see the Software License Integration Guide.

How It Works

StepWhat happensWho
1Buyer purchases your software on 3DIMLIBuyer
2Buyer receives an Order Item ID (license key) via email and in their dashboard3DIMLI
3Buyer enters the license key in your softwareBuyer
4Your software sends the key to your license server, which forwards it to 3DIMLI authenticated with your seller tokenYour software + license server
53DIMLI responds: valid: true/false + productName + licenseName3DIMLI
6Your software unlocks features based on the license tier, optionally enforces seat limitsYour software

3DIMLI answers one question: "Was this key legitimately purchased?"

You handle everything else: feature gating based on licenseName, and optionally seat counts, device limits, and concurrent sessions on your own server.


API Reference

Endpoint

POST
https://www.3dimli.com/api/software/v1/verify

This endpoint requires a seller license-verification API token in the Authorization header. Generate one from Dashboard → Settings → API Tokens. The token is shown once at creation time — copy and store it securely.


Request Headers

HeaderRequiredDescription
AuthorizationYesBearer <your_token> — your seller license-verification token
Content-TypeYesapplication/json

Request Body

Request
{
"key": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"product_slug": "/my-tool"
}
FieldTypeRequiredDescription
keystring (UUID)YesThe buyer's Order Item ID (their license key)
product_slugstringYesYour product's URL slug from the store (e.g., /my-tool)

The key must be a valid UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). The product_slug must be a non-empty string.

Slug format

The API normalizes the slug automatically. All of these are accepted: /my-tool, my-tool, software/my-tool.


Responses

200 OK
{
"valid": true,
"productName": "My Tool",
"licenseName": "Professional",
"variant": {
"title": "Windows"
}
}

Response Fields

FieldTypeDescription
validbooleantrue if the key is a legitimate, active, non-refunded purchase
productNamestring | nullThe product title on 3DIMLI (e.g., "My Tool")
licenseNamestring | nullThe license tier name the buyer purchased (e.g., "Standard License", "Team License")
variantobjectIncluded only when the purchased product has variants enabled. Contains title (e.g., the platform or edition the buyer selected)

When valid Returns false

A license is invalid if any of the following are true:

  • The Order Item ID does not exist
  • The order status is not COMPLETED
  • The order has been refunded
  • The product_slug does not match the purchased product
  • The product is not a Software type
Security

The API never leaks information about why a key is invalid. It always returns { "valid": false } with no additional details.


Validation Sequence

The API processes each request in this order:

StepCheckFailure response
1Authorization: Bearer <token> header present and non-empty401
2key is a valid UUID and product_slug is non-empty400 { valid: false }
3Token is valid and not revoked401
4Token owner has an active platform plan403 with subscription error
5Token owner is the seller of the product at software/<slug>403
6Client IP present400 { valid: false }
7Rate limit: 30 req / 60s / IP429 with Retry-After
8OrderItem exists with matching key AND product slug{ valid: false }
9Order item is in a verifiable state and not refunded{ valid: false }
10Product type is Software{ valid: false }

Steps 8–10 always return identical { valid: false } — no information is leaked about why it failed.


Status Codes

CodeMeaning
200Request processed. Check the valid field for the result
400Missing or invalid fields (key must be a valid UUID, product_slug must be non-empty)
401Missing, malformed, invalid, or revoked token
403Token is valid but you don't own this product, or your platform subscription has lapsed
429Rate limit exceeded. Check Retry-After header
500Server error. Retry later
503Rate limiter unavailable. Retry later

Rate Limiting

Limit30 requests per 60 seconds
ScopePer IP address (applied after token authentication)
HeadersRetry-After, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
Centralized license servers

If all your users' requests go through a single license server, those requests share one IP rate limit. Cache verification results on your server to stay within limits.


Generating Your Seller Token

  1. Sign in to your seller account on 3DIMLI.
  2. Open Dashboard → Settings → API Tokens.
  3. Generate a license-verification token.
  4. Copy the raw token shown in the one-time dialog and store it in a secret manager (for example, your license server's environment variables).
Keep the token server-side

Treat the token like a password. Do not ship it inside the buyer-facing software binary, where it can be extracted. Call the verify endpoint from your own license server (see examples below) and embed only the URL of your server in the software.

The token is bound to your seller account and can only verify keys for products you own. Verification requests for any other seller's product will return 403 Forbidden.


Finding Your Product Slug

Your product slug is the URL path of your product in the store. Find it in the browser address bar when viewing your product page:

https://www.3dimli.com/software/<your-product-slug>
└── product_slug ┘

For example, if your product page is at https://www.3dimli.com/software/my-tool, your product slug is /my-tool (the API also accepts my-tool or software/my-tool).

note

The product slug is not a secret — it's visible in the store URL. It acts as a scope guard so a key purchased for Product A cannot be used to activate Product B.


Integration Examples

Quick Test

cURL
curl -X POST https://www.3dimli.com/api/software/v1/verify \
-H "Authorization: Bearer <your_token>" \
-H "Content-Type: application/json" \
-d '{
"key": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"product_slug": "/my-tool"
}'

License Server with Seat Enforcement

The recommended pattern: your software talks to your license server, and your license server holds the seller token and calls 3DIMLI. This keeps the token off buyer machines.

A complete license server that verifies keys against 3DIMLI and enforces per-tier seat limits.

server.js
const express = require("express");
const app = express();
app.use(express.json());

// ── Config ──────────────────────────────────────────────
const PRODUCT_SLUG = "/my-tool";
const SELLER_TOKEN = process.env.DIMLI_SELLER_TOKEN; // never hardcode

const SEAT_LIMITS = {
"Standard License": 1,
"Team License": 5,
"Company License": 50,
"Enterprise License": -1, // unlimited
};

// ── Store (use a database in production) ────────────────
const activations = new Map(); // key -> { licenseName, devices: Set }

// ── Activate ────────────────────────────────────────────
app.post("/activate", async (req, res) => {
const { licenseKey, deviceId } = req.body;

if (!licenseKey || !deviceId) {
return res.status(400).json({ error: "licenseKey and deviceId required" });
}

// Verify with 3DIMLI
const response = await fetch(
"https://www.3dimli.com/api/software/v1/verify",
{
method: "POST",
headers: {
"Authorization": `Bearer ${SELLER_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ key: licenseKey, product_slug: PRODUCT_SLUG }),
}
);
const result = await response.json();

if (response.status === 401 || response.status === 403) {
// Misconfiguration on your side - alert ops, don't blame the user
console.error("3DIMLI auth failed:", result);
return res.status(503).json({ error: "License service unavailable" });
}

if (!result.valid) {
return res.status(403).json({ error: "Invalid license key" });
}

// Get or create activation record
let record = activations.get(licenseKey);
if (!record) {
record = { licenseName: result.licenseName, devices: new Set() };
activations.set(licenseKey, record);
}

// Already activated on this device
if (record.devices.has(deviceId)) {
return res.json({ activated: true, licenseName: record.licenseName });
}

// Enforce seat limit
const limit = SEAT_LIMITS[record.licenseName] ?? 1;
if (limit !== -1 && record.devices.size >= limit) {
return res.status(403).json({
error: `Seat limit reached (${limit}). Deactivate a device first.`,
});
}

record.devices.add(deviceId);
res.json({ activated: true, licenseName: record.licenseName });
});

// ── Deactivate ──────────────────────────────────────────
app.post("/deactivate", (req, res) => {
const { licenseKey, deviceId } = req.body;
const record = activations.get(licenseKey);
if (record) record.devices.delete(deviceId);
res.json({ deactivated: true });
});

app.listen(3000, () => console.log("License server on :3000"));

Client-Side Activation (In Your Software)

How the buyer's application calls your license server on startup:

license-client.js
const os = require("os");
const crypto = require("crypto");

function getDeviceId() {
const raw = `${os.hostname()}-${os.platform()}-${os.cpus()[0]?.model}`;
return crypto.createHash("sha256").update(raw).digest("hex").slice(0, 32);
}

async function activate(licenseKey) {
const response = await fetch("https://your-license-server.com/activate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ licenseKey, deviceId: getDeviceId() }),
});

const result = await response.json();

if (result.activated) {
// Save key locally so user doesn't re-enter it
saveToStorage("licenseKey", licenseKey);
unlockFeatures(result.licenseName);
return true;
}

showError(result.error);
return false;
}

Architecture

                             ┌─────────────────────────────────┐
│ YOUR LICENSE SERVER │
│ (holds seller Bearer token) │
┌───────────────┐ POST │ │ POST ┌──────────────┐
│ │ /activate│ 1. Receive key + deviceId │ + Bearer │ │
│ BUYER'S APP │─────────▶│ 2. Call 3DIMLI verify API ───────────▶│ 3DIMLI │
│ │ │ 3. Cache result │ │ API │
│ License key │◀─────────│ 4. Check seat limits │◀─────────│ │
│ + device ID │ Result │ 5. Register device │ valid │ /software/ │
└───────────────┘ │ 6. Return activated/denied │ + name │ v1/verify │
│ │ └──────────────┘
│ YOUR RESPONSIBILITY: │
│ • Seat enforcement │
│ • Device tracking │
│ • Session management │
│ • Feature gating │
│ • Offline grace periods │
│ • Token storage (server-side) │
└─────────────────────────────────┘

Best Practices

Caching

Don't call the 3DIMLI API on every app launch. Cache the result on your server and re-verify periodically.

Caching example
const PRODUCT_SLUG = "/my-tool";
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours

async function verifyWithCache(licenseKey) {
const cached = activations.get(licenseKey);

// Use cache if fresh
if (cached && Date.now() - cached.verifiedAt < CACHE_TTL) {
return cached;
}

// Re-verify with 3DIMLI
const res = await fetch("https://www.3dimli.com/api/software/v1/verify", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.DIMLI_SELLER_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ key: licenseKey, product_slug: PRODUCT_SLUG }),
});
const result = await res.json();

if (!result.valid) {
activations.delete(licenseKey); // Key revoked (e.g., refund)
return null;
}

// Update cache timestamp
if (cached) cached.verifiedAt = Date.now();
return cached;
}

Offline & Refunds

  • Offline grace period. If your server or 3DIMLI is unreachable, let the user continue for a set time (e.g., 7 days) before requiring re-verification.
  • Refund handling. When a buyer gets a refund, 3DIMLI returns valid: false. On your next re-verify cycle, revoke access gracefully with a clear message. Don't delete user data.

Device IDs

Generate stable, unique device identifiers using hardware fingerprints (hostname + OS + CPU). Store the ID locally so it persists across sessions.

Rate Limits

With 30 requests/minute per IP, a centralized server handling many users should cache aggressively. Batch or schedule re-verifications rather than verifying on every request.

Token Hygiene

  • Store the seller token in a secret manager or environment variable on your license server. Never commit it to source control.
  • Rotate the token from your API Tokens page if you suspect leakage.
  • A single token can verify any product you own — there's no need to mint a different token per product.

FAQ

Where does the buyer find their license key?

The Order Item ID is available in:

  1. The purchase confirmation email from 3DIMLI
  2. Dashboard → Orders → expand order → License Product ID
  3. Dashboard → Downloads → license details → Item ID

All locations include a copy button for convenience.

What happens if a buyer gets a refund?

The API returns { "valid": false } for refunded orders. Your license server should handle this on the next re-verification by revoking access gracefully.

Do I need an API token?

Yes. The software verify endpoint requires a seller license-verification token in the Authorization header. Generate one from Dashboard → Settings → API Tokens.

Can I put the token directly inside my software binary?

Strongly discouraged. Anyone who downloads your software can extract strings from the binary and use the token to verify keys for any of your products. Always proxy verification through a license server you control and keep the token there.

Can one key work across multiple products?

No. Each key (Order Item ID) is tied to a specific product. The product_slug in the request must match the product the buyer purchased.

What does licenseName return?

The exact name of the license tier the buyer selected during purchase, for example "Standard License", "Team License", or whatever custom names you defined when creating your product.

What does productName return?

The title of your product on 3DIMLI, for example "My Tool". This is included in valid responses so your software can display which product was activated.

What is variant?

If your product has variants enabled (e.g., Windows / macOS / Linux editions), the response includes the variant the buyer purchased. The field is omitted when the product has no variants.

What is product_slug and where do I find it?

The product slug is the URL path of your product in the 3DIMLI store. For example, if your product page is at https://www.3dimli.com/software/my-tool, the slug is /my-tool. It's not a secret — it prevents keys bought for one product from activating a different product.