Skip to content

Chainies

Chainies represent long-term objectives that group related chains together. For inviting other workspace members onto a Chainy, see Chainy Collaborators.

Endpoints

MethodPathDescriptionAuthRate Limit
GET/chainiesList all chainiesJWT60/min
GET/chainies/:idGet a single chainyJWT60/min
GET/chainies/:id/shareGet sharing info (visibility, share URL)JWT (owner only)60/min
POST/chainiesCreate a chainyJWT + Entitlement30/min
PATCH/chainies/:idUpdate a chainyJWT30/min
DELETE/chainies/:idDelete a chainyJWT30/min
PATCH/chainies/:id/statusUpdate chainy statusJWT30/min
POST/chainies/:id/archiveArchive a chainyJWT30/min
POST/chainies/:id/restoreRestore an archived chainyJWT30/min
GET/c/:urlShortResolve a shared chainy by short URLNone (public) or JWT60/min

Create Chainy

Entitlement: productivity.chainy.create

Request

FieldTypeRequiredDescription
titlestringYesChainy title (1–150 chars)
descriptionstringNoLong-form description (max 500 chars)
categorystringNoOne of: career, health, learning, relationships, finance, creativity, spirituality, other
statusstringNoOne of: active, paused, completed, abandoned, archived
isPublicbooleanNoWhether the chainy is publicly discoverable (default true)
visibilitystringNoprivate (default) | team | public. Setting public enables share URL generation on first PATCH.
colorHexstringNoHex color code in #RRGGBB format (default #10B981)
targetDatestringNoISO 8601 target completion date
instructionstringNoCustom AI instruction for this Chainy (max 2000 chars). Injected into the AI prompt only at session start.

Chainy responses also include an optional workspaceId field when the Chainy belongs to a workspace. Chainy Collaborators — inviting other workspace members onto this Chainy with a role — is only available when workspaceId is set.

Response

json
{
  "data": {
    "id": "cm5abc123",
    "title": "Learn Spanish",
    "description": "Achieve B2 fluency in Spanish by end of year",
    "colorHex": "#4A90D9",
    "status": "active",
    "visibility": "private",
    "urlShort": null,
    "targetDate": "2026-12-31T00:00:00.000Z",
    "createdAt": "2026-03-17T10:00:00.000Z"
  }
}

Save data.id — every other endpoint below refers to it as $CHAINY_ID.

Code Examples

bash
curl -X POST https://api.chainabit.com/api/v1/chainies \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Learn Spanish",
    "description": "Achieve B2 fluency in Spanish by end of year",
    "colorHex": "#4A90D9",
    "targetDate": "2026-12-31T00:00:00Z"
  }'
javascript
const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;

const response = await fetch(`${BASE_URL}/chainies`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "Learn Spanish",
    description: "Achieve B2 fluency in Spanish by end of year",
    colorHex: "#4A90D9",
    targetDate: "2026-12-31T00:00:00Z",
  }),
});
const { data } = await response.json();
python
import requests, os

BASE_URL = os.environ["BASE_URL"]
TOKEN = os.environ["TOKEN"]

response = requests.post(
    f"{BASE_URL}/chainies",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "title": "Learn Spanish",
        "description": "Achieve B2 fluency in Spanish by end of year",
        "colorHex": "#4A90D9",
        "targetDate": "2026-12-31T00:00:00Z",
    },
)
data = response.json()["data"]

Update Chainy Status

Request

FieldTypeRequiredDescription
statusstringYesOne of: active, paused, completed, abandoned, archived

Use the id from Create Chainy's response (data.id) as $CHAINY_ID.

Response

json
{
  "data": {
    "id": "cm5abc123",
    "title": "Learn Spanish",
    "status": "completed",
    "createdAt": "2026-03-17T10:00:00.000Z"
  }
}

Code Examples

bash
curl -X PATCH https://api.chainabit.com/api/v1/chainies/$CHAINY_ID/status \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "status": "completed" }'
javascript
const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const CHAINY_ID = process.env.CHAINY_ID; // from Create Chainy's data.id

const response = await fetch(`${BASE_URL}/chainies/${CHAINY_ID}/status`, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ status: "completed" }),
});
const { data } = await response.json();
python
import requests, os

response = requests.patch(
    f"{os.environ['BASE_URL']}/chainies/{os.environ['CHAINY_ID']}/status",
    headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
    json={"status": "completed"},
)
data = response.json()["data"]

List Chainies

Request

Response

json
{
  "data": [
    {
      "id": "cm5abc123",
      "title": "Learn Spanish",
      "status": "active",
      "colorHex": "#4A90D9",
      "createdAt": "2026-03-17T10:00:00.000Z"
    }
  ],
  "meta": { "total": 1 }
}

Code Examples

bash
curl https://api.chainabit.com/api/v1/chainies \
  -H "Authorization: Bearer $TOKEN"
javascript
const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;

const response = await fetch(`${BASE_URL}/chainies`, {
  headers: { Authorization: `Bearer ${TOKEN}` },
});
const { data } = await response.json();
python
import requests, os

response = requests.get(
    f"{os.environ['BASE_URL']}/chainies",
    headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
)
data = response.json()["data"]

Archive / Restore

Request

Response

json
{
  "data": {
    "id": "cm5abc123",
    "status": "archived",
    "createdAt": "2026-03-17T10:00:00.000Z"
  }
}

Code Examples

bash
# Archive
curl -X POST https://api.chainabit.com/api/v1/chainies/$CHAINY_ID/archive \
  -H "Authorization: Bearer $TOKEN"

# Restore
curl -X POST https://api.chainabit.com/api/v1/chainies/$CHAINY_ID/restore \
  -H "Authorization: Bearer $TOKEN"
javascript
const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const CHAINY_ID = process.env.CHAINY_ID; // from Create Chainy's data.id

// Archive
await fetch(`${BASE_URL}/chainies/${CHAINY_ID}/archive`, {
  method: "POST",
  headers: { Authorization: `Bearer ${TOKEN}` },
});

// Restore
await fetch(`${BASE_URL}/chainies/${CHAINY_ID}/restore`, {
  method: "POST",
  headers: { Authorization: `Bearer ${TOKEN}` },
});
python
import requests, os

headers = {"Authorization": f"Bearer {os.environ['TOKEN']}"}

# Archive
requests.post(f"{os.environ['BASE_URL']}/chainies/{os.environ['CHAINY_ID']}/archive", headers=headers)

# Restore
requests.post(f"{os.environ['BASE_URL']}/chainies/{os.environ['CHAINY_ID']}/restore", headers=headers)

Sharing a Chainy

Chainies support three visibility levels:

visibilityWho can access via share URL
private (default)Owner only (requires JWT)
teamWorkspace members only (requires JWT)
publicAnyone — no authentication required

When you set visibility to public, a short URL is generated: https://chainabit.com/c/:urlShort. Setting visibility back to private or team permanently deactivates the token — a new one is generated on next publish.

Make a Chainy Public

Set visibility: "public" in a PATCH request. A urlShort token is generated automatically on first publish.

Request

  • Example body:
    json
    { "visibility": "public" }

Response

The response includes urlShort in the chainy object:

json
{
  "data": {
    "id": "cm5abc123",
    "title": "Learn Spanish",
    "visibility": "public",
    "urlShort": "a3f2b9c1e4",
    "createdAt": "2026-03-17T10:00:00.000Z"
  }
}

Code Examples

bash
curl -X PATCH https://api.chainabit.com/api/v1/chainies/$CHAINY_ID \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "visibility": "public" }'
javascript
const BASE_URL = process.env.BASE_URL;
const TOKEN = process.env.TOKEN;
const CHAINY_ID = process.env.CHAINY_ID; // from Create Chainy's data.id

const response = await fetch(`${BASE_URL}/chainies/${CHAINY_ID}`, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ visibility: "public" }),
});
const { data } = await response.json();
// data.urlShort — use this in your share URL
python
import requests, os

response = requests.patch(
    f"{os.environ['BASE_URL']}/chainies/{os.environ['CHAINY_ID']}",
    headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
    json={"visibility": "public"},
)
data = response.json()["data"]
# data["urlShort"]

Get Share Info

Retrieve the current visibility, short token, and full share URL for a chainy you own.

Request

Response

json
{
  "data": {
    "visibility": "public",
    "urlShort": "a3f2b9c1e4",
    "shareUrl": "https://chainabit.com/c/a3f2b9c1e4"
  }
}

Code Examples

bash
curl https://api.chainabit.com/api/v1/chainies/$CHAINY_ID/share \
  -H "Authorization: Bearer $TOKEN"
javascript
const response = await fetch(`${BASE_URL}/chainies/${CHAINY_ID}/share`, {
  headers: { Authorization: `Bearer ${TOKEN}` },
});
const { data } = await response.json();
console.log(data.shareUrl); // https://chainabit.com/c/a3f2b9c1e4
python
import requests, os

response = requests.get(
    f"{os.environ['BASE_URL']}/chainies/{os.environ['CHAINY_ID']}/share",
    headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
)
data = response.json()["data"]
print(data["shareUrl"])

Resolve a Shared Chainy

Access a shared chainy using the short URL. No authentication required for public chainies.

Request

Response

json
{
  "data": {
    "id": "cm5abc123",
    "title": "Learn Spanish",
    "description": "Achieve B2 fluency in Spanish by end of year",
    "visibility": "public",
    "urlShort": "a3f2b9c1e4",
    "status": "active",
    "colorHex": "#4A90D9",
    "createdAt": "2026-03-17T10:00:00.000Z"
  }
}

Private and team chainies return 404 Not Found when accessed without a valid JWT or without the appropriate workspace membership. This applies even when the URL is correct — existence is never confirmed to unauthorized callers.

Code Examples

bash
# Public chainy — no auth needed
curl https://api.chainabit.com/api/v1/c/a3f2b9c1e4

# Private/team chainy — JWT required
curl https://api.chainabit.com/api/v1/c/a3f2b9c1e4 \
  -H "Authorization: Bearer $TOKEN"
javascript
// Public
const response = await fetch(`${BASE_URL}/c/a3f2b9c1e4`);
const { data } = await response.json();

// Private/team
const response = await fetch(`${BASE_URL}/c/a3f2b9c1e4`, {
  headers: { Authorization: `Bearer ${TOKEN}` },
});
const { data } = await response.json();
python
import requests, os

# Public
response = requests.get(f"{os.environ['BASE_URL']}/c/a3f2b9c1e4")

# Private/team
response = requests.get(
    f"{os.environ['BASE_URL']}/c/a3f2b9c1e4",
    headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
)
data = response.json()["data"]

Regenerate the Share URL

If you want to invalidate the current share URL and create a new one, pass regenerateSlug: true. The previous URL stops working immediately.

Request

  • Example body:
    json
    { "regenerateSlug": true }

Response

Warning: Regenerating the slug immediately invalidates the previous share URL. Anyone who saved the old link will get a 404. Notify collaborators before regenerating.

Code Examples

bash
curl -X PATCH https://api.chainabit.com/api/v1/chainies/$CHAINY_ID \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "regenerateSlug": true }'
javascript
await fetch(`${BASE_URL}/chainies/${CHAINY_ID}`, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ regenerateSlug: true }),
});

Built with purpose.