Integration API

Integration reference for your software. There are two groups of endpoints using two different credentials: the shipped client uses X-Api-Key, your own team uses X-Dev-Token. Both come from Admin → Tools.

Base URL https://cyanpro.app

Endpoint overview

MethodPath AuthWhat it does
GET /api/v1/ping API key Check the API key still works
POST /api/v1/auth/login API key Sign a user in from your software; returns tokens and licence
POST /api/v1/license/verify API key + Bearer Check the licence is still valid; call periodically while running
POST /api/v1/auth/refresh API key Trade a refresh token for a new access token
POST /api/v1/auth/logout API key + Bearer Give back one device slot
GET /api/v1/account/profile API key + Bearer Details of the signed-in account
GET /api/v1/version API key Ask whether a newer build exists
GET /api/v1/dev/product Dev key Read the product
PATCH /api/v1/dev/product Dev key Update description, platforms, download link
POST /api/v1/dev/releases Dev key Publish a new version
GET /api/v1/dev/releases Dev key Release history

How it works

  1. Your software collects a stable fingerprint of the machine (disk serial, MAC, machine GUID…) and hashes it.
  2. It calls POST /api/v1/auth/login with the email, password and fingerprint.
  3. It receives an access_token (1 hour) and a refresh_token (90 days) — store the refresh token in your app’s private data area.
  4. On every start, and every 6 hours after that, it calls POST /api/v1/license/verify to confirm the licence is still valid.
  5. When the access token expires, it trades the refresh token for a new one at POST /api/v1/auth/refresh.

1. Signing in from your software

Returns the user’s currently valid licence for the product tied to the API key.

POST https://cyanpro.app/api/v1/auth/login
Content-Type: application/json
X-Api-Key: cyk_live_xxxxxxxx

{
  "email": "user@example.com",
  "password": "••••••••",
  "fingerprint": "a3f1c8...",        // SHA-256 hashed on the client
  "device_name": "Office PC",
  "platform": "Windows 11",
  "app_version": "2.4.1"
}
200 OK
{
  "ok": true,
  "access_token": "eyJhbGciOi...",
  "refresh_token": "9f2b...",
  "expires_in": 3600,
  "license": {
    "key": "CYAN-4K2P-9XQW-7MTB-R3VD",
    "status": "active",
    "plan": "Pro",
    "is_trial": false,            // true while the free trial is running
    "expires_at": 1792454400,
    "days_left": 27,
    "max_devices": 3,
    "devices_in_use": 2,
    "entitlements": { "export_pdf": true, "max_projects": 50 }
  },
  "account": { "email": "user@example.com", "full_name": "Nguyen Van A" }
}

Common errors: invalid_credentials, email_not_verified, no_license, license_expired, device_limit_reached. When is_trial is true, your software should show the days remaining and an upgrade prompt rather than staying silent.

2. Checking the licence

Call this periodically while the software runs. It is the single source of truth about expiry.

POST https://cyanpro.app/api/v1/license/verify
X-Api-Key: cyk_live_xxxxxxxx
Authorization: Bearer <access_token>

{ "fingerprint": "a3f1c8...", "app_version": "2.4.1" }
200 OK
{ "ok": true, "valid": true, "license": { ... }, "server_time": 1792108800 }

200 OK — licence expired (still 200 — "valid" is the field to read)
{ "ok": true, "valid": false, "reason": "license_expired", "license": { ... } }

3. Refreshing the access token

POST https://cyanpro.app/api/v1/auth/refresh
X-Api-Key: cyk_live_xxxxxxxx

{ "refresh_token": "9f2b..." }

4. Releasing a device

Frees one device slot so another machine can use it.

POST https://cyanpro.app/api/v1/auth/logout
X-Api-Key: cyk_live_xxxxxxxx
Authorization: Bearer <access_token>

5. Checking for updates

The installed software calls this at startup to find out whether a newer build exists. Read-only, and the only release-related thing the shipped API key may touch.

GET https://cyanpro.app/api/v1/version?current=1.0.0
X-Api-Key: cyk_live_xxxxxxxx
200 OK
{
  "ok": true,
  "latest_version": "1.1.0",
  "update_available": true,
  "is_mandatory": false,
  "download_url": "https://example.com/setup-1.1.0.exe",
  "notes": "- Sửa lỗi đăng nhập\n- Tăng tốc khởi động",
  "released_at": 1792470000,
  "server_time": 1792470100
}

Send current as the version you are running. Without it, update_available comes back null — the system does not guess. Comparison is numeric, so 1.10.0 is understood to be newer than 1.9.0.

Developer API

For your own team to edit the product description and publish releases without opening the admin area. It uses a different credential from the API key above.

Two kinds of key — never mix them up
X-Api-Key (cyk_live_…) ships inside the installer you give customers; anyone can pull it out of the executable, which is why it can only sign in and ask about licence expiry. X-Dev-Token (cyd_live_…) can change data, so it stays on developer machines and in your build system — never inside a build you ship. Create one under Admin → Tools → Developer keys. Each key reaches exactly one product.

Read the product

GET https://cyanpro.app/api/v1/dev/product
X-Dev-Token: cyd_live_xxxxxxxx

Update the product

Send only the fields you want changed. slug cannot be changed here (it would break every public link pointing at the product) and neither can visibility — both belong in the admin area.

PATCH https://cyanpro.app/api/v1/dev/product
X-Dev-Token: cyd_live_xxxxxxxx
Content-Type: application/json

{
  "tagline": "Bộ công cụ desktop cho công việc hằng ngày",
  "tagline_en": "A desktop toolkit for everyday work",
  "description": "Mô tả dài hơn, hiện ở trang chi tiết.",
  "platforms": ["Windows", "macOS"],
  "download_url": "https://example.com/setup.exe"
}

Publish a release

Call this from your build system once the package is ready. Each version number can only be published once; republishing over an existing one is refused.

POST https://cyanpro.app/api/v1/dev/releases
X-Dev-Token: cyd_live_xxxxxxxx
Content-Type: application/json

{
  "version": "1.1.0",
  "notes": "- Sửa lỗi đăng nhập\n- Tăng tốc khởi động",
  "notes_en": "- Fixed a sign-in bug\n- Faster startup",
  "download_url": "https://example.com/setup-1.1.0.exe",
  "is_mandatory": false
}
201 Created
{
  "ok": true,
  "release": {
    "version": "1.1.0",
    "is_latest": true,
    "is_mandatory": false,
    "download_url": "https://example.com/setup-1.1.0.exe",
    "released_at": 1792470000
  }
}

A patch for an older branch is still recorded but does not drag the current version backwards: publish 1.9.5 after 2.0.0 and is_latest comes back false, so customers on 2.0.0 are never prompted to downgrade.

Release history

GET https://cyanpro.app/api/v1/dev/releases
X-Dev-Token: cyd_live_xxxxxxxx

Error codes

Every error response has the same shape: { "ok": false, "error": "error_code", "message": "description" }. Branch on error, never on message — the wording may change, the code will not.

CodeHTTPMeaning and what to do
missing_api_key 401 The X-Api-Key header is missing. Check how the build is configured.
invalid_api_key 401 The API key is wrong or has been revoked. Report a configuration error; do not retry in a loop.
missing_dev_token 401 X-Dev-Token is missing. The shipped API key does not work on the /dev branch.
invalid_dev_token 401 The developer key is wrong or has been revoked.
invalid_credentials 401 Wrong email or password. Show one generic message; do not say which was wrong.
email_not_verified 403 The account has not verified its email yet. Point the user at their inbox.
no_license 403 The account has no licence for this product. Send them to the pricing page.
license_expired 403 The licence has expired. Send them to the renewal page.
device_limit_reached 403 All device slots on this plan are in use. Tell them to release an old machine from their dashboard.
device_revoked 403 This device was released. Clear the stored tokens and ask the user to sign in again.
missing_token 401 The Authorization header is missing. Send the access token.
invalid_token 401 The access token is invalid or expired. Trade the refresh token for a new one.
version_exists 409 That version number has already been published. Use a new one.
invalid_version 400 The version string is not valid. Letters, digits and . - + only.
rate_limited 429 Too many calls. Wait the number of seconds given in Retry-After, then try again.

Rate limits

Hitting a limit returns HTTP 429 with a Retry-After header in seconds. Do not retry immediately — wait that long.

OperationLimitCounted per
Sign-in from software 15 / 15 min account
Sign-in from software 100 / 15 min IP address
Licence check 120 / 1 min device
Developer API 120 / 1 hour dev key

Implementation rules

  • Never ship the API secret inside the client. The API key is enough for a client; the secret belongs on your own backend, if you have one.
  • Always trust the server’s clock (server_time), never the client’s — users can change theirs.
  • Allow limited offline use: remember the last successful check and keep running for 3–7 days without a network, then require a reconnect.
  • The fingerprint must survive software updates but change when the machine does. Machine GUID plus motherboard serial is a good combination.
  • Every error response has the shape { "ok": false, "error": "error_code", "message": "description" }.

Full sample code for Node.js/Electron and .NET lives in the project’s client-sdk/ folder.