Base host: https://api.termly.io (HTTPS only) All endpoints sit under: /v1/
For a test account, request one from Termly.
Docs: https://docs.termly.io
Step 1. Get the API keys
Partner keys are issued manually by Termly (they are not self-serve). The partner receives two values:
-
Public Key (identifies the partner, sent in the
Authorizationheader) - Private Key (the signing secret, never sent over the wire)
Action: store both securely. In Postman, add them as environment variables named exactly termly_public_key and termly_private_key (Step 3 reads them by those names).
Reference: https://docs.termly.io/introduction/authentication/
Step 2. Know what every request needs
Every request to api.termly.io carries two Termly headers:
| Header | Value |
|---|---|
X-Termly-Timestamp |
UTC timestamp, format %Y%m%dT%H%M%SZ
|
Authorization |
TermlyV1, PublicKey=<public key>, Signature=<signature> |
The partner does not set these by hand. The only header they set manually is Content-Type: application/json. The before-request script in Step 3 adds X-Termly-Timestamp and Authorization automatically at send time. Everything else (Host, Content-Length, User-Agent, etc.) is generated by Postman.
Notes:
- The timestamp must be within 15 minutes of server time or the request is rejected.
- The timestamp is part of the signature, so it cannot be altered after signing.
Reference: https://docs.termly.io/introduction/make-a-request/
Step 3. Paste the before-request script (no signature building)
The partner does not build a signature. They do a one-time Postman setup, then every request signs itself.
- Set the two environment variables from Step 1:
termly_public_key,termly_private_key. - On the request, set one header:
Content-Type: application/json. - Open the Scripts > Before request tab and paste the script below.
That is the whole setup. After this, changing the method, URL, or body needs no auth changes.
var publicKey = pm.environment.get("termly_public_key");
var privateKey = pm.environment.get("termly_private_key");
var service = "termly"
var region = "default"
encodedQuery = ""
if (["GET", "DELETE"].includes(pm.request.method)) {
let body = pm.request.body.raw
if (body){
jsonBody = JSON.stringify(JSON.parse(body))
encodedQuery = encodeURIComponent(jsonBody)
}
pm.request.body.raw = ""
pm.request.addQueryParams("query=" + encodedQuery);
}
var hashOfBody = CryptoJS.SHA256(pm.request.body.raw).toString();
var timeStamp = new Date().toISOString().split('.')[0] + "Z";
timeStamp = timeStamp.replace(/[^a-zA-Z0-9 ]/g, "");
var canonicalRequest = pm.request.method + "\n"
canonicalRequest = canonicalRequest + pm.request.url.host.join('.') + "\n"
canonicalRequest = canonicalRequest + "/" + pm.request.url.path.join("/") + "\n"
canonicalRequest = canonicalRequest + encodedQuery + "\n"
canonicalRequest = canonicalRequest + timeStamp + "\n"
canonicalRequest = canonicalRequest + hashOfBody
var derivedKey = CryptoJS.HmacSHA256(timeStamp, privateKey);
derivedKey = CryptoJS.HmacSHA256(region, derivedKey);
derivedKey = CryptoJS.HmacSHA256(service, derivedKey);
var signature = CryptoJS.HmacSHA256(canonicalRequest, derivedKey);
signature = CryptoJS.enc.Hex.stringify(signature)
pm.request.headers.add({
key: "Authorization",
value: "TermlyV1, PublicKey=" + publicKey + ",Signature=" + signature
});
pm.request.headers.add({
key: "X-Termly-Timestamp",
value: timeStamp
});Step 4. How GET / DELETE filtering works (the query parameter)
The script handles the encoding, so the partner just puts a JSON body on GET and DELETE requests. Good to understand what it becomes:
- GET and DELETE have no payload. They select records with a
querystring parameter: a URL-encoded JSON array of objects, even for a single record. - Decoded:
[{ "account_id": "acct_1234" }] - On the wire:
GET https://api.termly.io/v1/websites?query=%5B%7B%22account_id%22%3A%22acct_1234%22%7D%5D
Rules:
-
queryandpagingare mutually exclusive on GET. Sending both is rejected. - DELETE supports
queryonly and deletes every record that matches. - Add
limiton a GET to set the page size. - Include several objects in the array to look up several records in one request (batching).
References: https://docs.termly.io/other/query/ and https://docs.termly.io/other/results-paging/
Step 5. Discover the accounts / websites you manage
Start automation by pulling the websites the key has access to. This gives you the account_id and website_id values used everywhere else.
-
GET
https://api.termly.io/v1/websites
Reference: https://docs.termly.io/endpoints/websites-get/
Step 6. Provision and manage websites
-
GET
/v1/websites(list / look up) -
PUT
/v1/websites(update) -
POST
/v1/websites(create) -
DELETE
/v1/websites(remove)
Reference: https://docs.termly.io/endpoints/websites-post/
Step 7. Manage collaborators (access control)
-
GET
/v1/collaborators -
PUT
/v1/collaborators -
POST
/v1/collaborators -
DELETE
/v1/collaborators
Body example (POST):
[{ "account_id": "acct_1234", "email": "user@example.com", "role": "admin" }]References: https://docs.termly.io/endpoints/collaborators-post/ and https://docs.termly.io/other/collaborator-roles/
Step 8. Configure the CMP banner and consent themes
Banners:
-
GET
/v1/banners -
PUT
/v1/banners
Custom Consent Themes:
-
GET
/v1/custom-consent-themes -
PUT
/v1/custom-consent-themes -
POST
/v1/custom-consent-themes -
DELETE
/v1/custom-consent-themes
References: https://docs.termly.io/endpoints/banners-get/ and https://docs.termly.io/quickstart/cmp-integration/
Step 9. Manage the cookie dictionary
-
GET
/v1/cookies -
PUT
/v1/cookies -
POST
/v1/cookies -
DELETE
/v1/cookies
Reference: https://docs.termly.io/endpoints/cookies-get/
Step 10. Trigger scans and pull reports
-
POST
/v1/scan(trigger a scan) -
GET
/v1/scan-reports(retrieve scan results)
References: https://docs.termly.io/endpoints/trigger-scan/ and https://docs.termly.io/endpoints/scan-reports-get/
Step 11. Publish documents
- GET document preview: https://docs.termly.io/endpoints/document-preview/
- POST publish cookie policy: https://docs.termly.io/endpoints/publish-cookie-policy/
Reference: batching and response codes
Create and update endpoints accept multiple objects in one request, and each is processed independently.
| Code | Meaning |
|---|---|
200 |
Succeeded for every object in the batch |
207 |
Succeeded for at least one object, not all (check each returned object) |
400 |
Failed for every object in the batch |
403 |
Authorization failed, nothing processed (all-or-nothing on auth) |
500 |
Internal API error (contact Termly) |
Reference: https://docs.termly.io/introduction/make-a-request/#batch-request-processing
Appendix: signing outside Postman (curl / Node / Python)
For partners not using Postman, the signature is built from the same values the script uses.
Canonical request (parts joined by a single newline \n):
HTTP Method + '\n' + Host + '\n' + Path + '\n' + [query value | paging value | empty string] + '\n' + X-Termly-Timestamp value + '\n' + HexEncode(SHA256(RequestBody)) // empty string body if none
Derived key, then signature:
secret = <private key> secret = HMAC-SHA256(secret, <X-Termly-Timestamp>) secret = HMAC-SHA256(secret, 'default') // region secret = HMAC-SHA256(secret, 'termly') // service signature = HMAC-SHA256(secret, <canonical request>) // hex encoded
Header:
Authorization: TermlyV1, PublicKey=<public key>, Signature=<signature>
Reference: https://docs.termly.io/other/signature/