Server-to-server only. Use this surface to manage Connek resources (clients, leads, services, bookings, quotes, invoices) on behalf of a business you have a partner key for. Browser-side calls are never supported — the secret must stay on your backend.
Every request must carry three headers. The signature proves you hold the secret without putting the secret on the wire.
Canonical string (newline-joined):
{METHOD}
{PATH}
{UNIX_TIMESTAMP}
{HEX(SHA256(BODY))}
METHOD=GET
PATH=/v1/services
TS=$(date +%s)
BODY=""
BODY_SHA=$(printf "%s" "$BODY" | openssl dgst -sha256 -hex | awk '{print $2}')
CANONICAL=$(printf "%s\n%s\n%s\n%s" "$METHOD" "$PATH" "$TS" "$BODY_SHA")
SIG=$(printf "%s" "$CANONICAL" | openssl dgst -sha256 -hmac "$CONNEK_SECRET" -hex | awk '{print $2}')
curl -X "$METHOD" "https://api.dev.connek.ca$PATH" \
-H "X-Connek-Key-Id: $CONNEK_KEY_ID" \
-H "X-Connek-Timestamp: $TS" \
-H "X-Connek-Signature: $SIG"import hashlib, hmac, json, os, time, requests
KEY_ID = os.environ["CONNEK_KEY_ID"]
SECRET = os.environ["CONNEK_SECRET"]
BASE = "https://api.dev.connek.ca"
def sign(method: str, path: str, body: bytes = b"") -> dict[str, str]:
ts = str(int(time.time()))
body_sha = hashlib.sha256(body).hexdigest()
canonical = f"{method}\n{path}\n{ts}\n{body_sha}".encode()
sig = hmac.new(SECRET.encode(), canonical, hashlib.sha256).hexdigest()
return {
"X-Connek-Key-Id": KEY_ID,
"X-Connek-Timestamp": ts,
"X-Connek-Signature": sig,
}
r = requests.get(BASE + "/v1/services", headers=sign("GET", "/v1/services"))
print(r.status_code, r.json())import { createHash, createHmac } from "node:crypto";
const KEY_ID = process.env.CONNEK_KEY_ID;
const SECRET = process.env.CONNEK_SECRET;
const BASE = "https://api.dev.connek.ca";
function sign(method, path, body = "") {
const ts = String(Math.floor(Date.now() / 1000));
const bodySha = createHash("sha256").update(body, "utf8").digest("hex");
const canonical = `${method}\n${path}\n${ts}\n${bodySha}`;
const sig = createHmac("sha256", SECRET).update(canonical, "utf8").digest("hex");
return {
"X-Connek-Key-Id": KEY_ID,
"X-Connek-Timestamp": ts,
"X-Connek-Signature": sig,
};
}
const r = await fetch(`${BASE}/v1/services`, { headers: sign("GET", "/v1/services") });
console.log(r.status, await r.json());
All paths are under /v1. Full schema in
/docs / openapi.json.
| Method | Path | Scope |
|---|---|---|
| GET | /v1/services | services:read |
| POST | /v1/services | services:write |
| GET | /v1/clients | clients:read |
| POST | /v1/clients | clients:write |
| GET | /v1/leads | leads:read |
| POST | /v1/leads | leads:write |
| GET | /v1/bookings | bookings:read |
| POST | /v1/bookings | bookings:write |
| GET | /v1/quotes | quotes:read |
| POST | /v1/quotes | quotes:write |
| GET | /v1/invoices | invoices:read |
| POST | /v1/invoices | invoices:write |
| GET | /v1/availability | availability:read |
All non-2xx responses follow the same shape:
{
"detail": {
"error": {
"code": "missing_field",
"message": "client_email is required"
}
}
}
Status codes you should handle:
401 (bad/expired signature),
403 (key missing scope),
404 (no resource),
409 (state conflict),
422 (validation),
429 (rate limited — 100 req/min/key),
5xx (back off and retry).
Reference completa por tema en el repo: