Multi-language Examples

Reserve and unreserve from curl, Python, Node.js, Go, and C#. No SDK required — it's just HTTP.

curl

Reserve

curl -X POST https://cooplocker.com/api/v1/Reservation/reserve \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[{"requester":"my-app","resource":"my-lock","ttlSeconds":60}]'

Unreserve

curl -X POST https://cooplocker.com/api/v1/Reservation/unreserve \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[{"requester":"my-app","resource":"my-lock","releaseToken":"TOKEN_FROM_RESERVE"}]'

Check

curl https://cooplocker.com/api/v1/Reservation/check/my-lock \
  -H "X-Api-Key: YOUR_API_KEY"

Python (requests)

No SDK needed — the standard requests library is all you need.

Reserve

import requests

API_KEY = "YOUR_API_KEY"
BASE    = "https://cooplocker.com/api/v1"
HEADERS = {"X-Api-Key": API_KEY, "Content-Type": "application/json"}

resp = requests.post(
    f"{BASE}/Reservation/reserve",
    headers=HEADERS,
    json=[{"requester": "my-app", "resource": "my-lock", "ttlSeconds": 60}],
)
resp.raise_for_status()

results = resp.json()
result  = results[0]
print(result["status"])          # "ACQUIRED"
token   = result["releaseToken"] # save this

Unreserve

resp = requests.post(
    f"{BASE}/Reservation/unreserve",
    headers=HEADERS,
    json=[{
        "requester":    "my-app",
        "resource":     "my-lock",
        "releaseToken": token,
    }],
)
resp.raise_for_status()
print(resp.json()[0]["status"])  # "RELINQUISHED"

Node.js (native fetch)

Requires Node.js 18+. No additional packages needed.

Reserve

const API_KEY = "YOUR_API_KEY";
const BASE    = "https://cooplocker.com/api/v1";
const headers = { "X-Api-Key": API_KEY, "Content-Type": "application/json" };

const reserveResp = await fetch(`${BASE}/Reservation/reserve`, {
  method:  "POST",
  headers,
  body: JSON.stringify([
    { requester: "my-app", resource: "my-lock", ttlSeconds: 60 }
  ]),
});

if (!reserveResp.ok) throw new Error(`Reserve failed: ${reserveResp.status}`);

const [result] = await reserveResp.json();
console.log(result.status);          // "ACQUIRED"
const token = result.releaseToken;   // save this

Unreserve

const unreserveResp = await fetch(`${BASE}/Reservation/unreserve`, {
  method:  "POST",
  headers,
  body: JSON.stringify([
    { requester: "my-app", resource: "my-lock", releaseToken: token }
  ]),
});

if (!unreserveResp.ok) throw new Error(`Unreserve failed: ${unreserveResp.status}`);
const [unreserveResult] = await unreserveResp.json();
console.log(unreserveResult.status); // "RELINQUISHED"

Go (net/http)

Reserve

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)

const apiKey = "YOUR_API_KEY"
const base   = "https://cooplocker.com/api/v1"

func reserve(requester, resource string, ttl int) (string, error) {
    body, _ := json.Marshal([]map[string]any{
        {"requester": requester, "resource": resource, "ttlSeconds": ttl},
    })

    req, _ := http.NewRequest("POST", base+"/Reservation/reserve", bytes.NewReader(body))
    req.Header.Set("X-Api-Key", apiKey)
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    var results []map[string]any
    json.NewDecoder(resp.Body).Decode(&results)

    token, _ := results[0]["releaseToken"].(string)
    fmt.Println("status:", results[0]["status"])
    return token, nil
}

Unreserve

func unreserve(requester, resource, token string) error {
    body, _ := json.Marshal([]map[string]any{
        {"requester": requester, "resource": resource, "releaseToken": token},
    })

    req, _ := http.NewRequest("POST", base+"/Reservation/unreserve", bytes.NewReader(body))
    req.Header.Set("X-Api-Key", apiKey)
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    var results []map[string]any
    json.NewDecoder(resp.Body).Decode(&results)
    fmt.Println("status:", results[0]["status"])
    return nil
}

C# (HttpClient)

Reserve

using System.Net.Http.Json;

var client  = new HttpClient();
var apiKey  = "YOUR_API_KEY";
var baseUrl = "https://cooplocker.com/api/v1";

client.DefaultRequestHeaders.Add("X-Api-Key", apiKey);

var payload = new[]
{
    new { requester = "my-app", resource = "my-lock", ttlSeconds = 60 }
};

var response = await client.PostAsJsonAsync($"{baseUrl}/Reservation/reserve", payload);
response.EnsureSuccessStatusCode();

var results = await response.Content.ReadFromJsonAsync<List<ReservationResult>>();
var token   = results![0].ReleaseToken;
Console.WriteLine(results[0].Status); // ACQUIRED

record ReservationResult(string Resource, string Requester, string Status,
                          string Explanation, string? ReleaseToken);

Unreserve

var release = new[]
{
    new { requester = "my-app", resource = "my-lock", releaseToken = token }
};

var resp = await client.PostAsJsonAsync($"{baseUrl}/Reservation/unreserve", release);
resp.EnsureSuccessStatusCode();

var releaseResults = await resp.Content.ReadFromJsonAsync<List<ReservationResult>>();
Console.WriteLine(releaseResults![0].Status); // RELINQUISHED

Conflict strategy examples

Use POST /api/v1/Reservation/transactions when you need atomic multi-resource semantics. Pass one or more transaction objects, each with a method and a list of requests.

ALL_OR_NONE — both locks or nothing

curl -X POST https://cooplocker.com/api/v1/Reservation/transactions \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[{
    "method":      "ALL_OR_NONE",
    "requestType": "RESERVE",
    "requests": [
      {"requester":"svc-a","resource":"db-primary"},
      {"requester":"svc-a","resource":"db-replica"}
    ]
  }]'

If db-replica is held, neither lock is acquired and the transaction rolls back.

FIRST — claim the first available worker slot

curl -X POST https://cooplocker.com/api/v1/Reservation/transactions \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[{
    "method":      "FIRST",
    "requestType": "RESERVE",
    "requests": [
      {"requester":"job-99","resource":"worker-1"},
      {"requester":"job-99","resource":"worker-2"},
      {"requester":"job-99","resource":"worker-3"}
    ]
  }]'

Claims the first available worker and stops — no need to check which one is free first.

AS_MANY_AS_POSSIBLE — grab every available slot

curl -X POST https://cooplocker.com/api/v1/Reservation/transactions \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[{
    "method":      "AS_MANY_AS_POSSIBLE",
    "requestType": "RESERVE",
    "requests": [
      {"requester":"batch-runner","resource":"slot-A"},
      {"requester":"batch-runner","resource":"slot-B"},
      {"requester":"batch-runner","resource":"slot-C"}
    ]
  }]'

Acquires whichever slots are free and skips the rest — useful for bulk claiming.