Table Links
A table link is a url a user attaches to a table — a source, a reference, or anything else worth pointing at from the conversation the table is having.
Links are owned per contribution rather than per table. Any authenticated user may attach a link to any table, and the link belongs to whoever added it, so one table can carry links from several different user tags. This is a deliberate departure from the table itself, where writes stay with the table's owner: the link's owner is the adder, and only that owner can delete it — not even the table's owner can.
Links are not nested into the table object. A table that has collected many links would otherwise bloat every list, search, and trending response, so they are fetched from their own endpoints.
The table link object
| Attribute | Type | Description |
|---|---|---|
| id | uuid | The unique identifier of the link. |
| table_id | uuid | The unique identifier of the table the link is attached to. |
| url | string | The url. Up to 2048 characters. Not validated as a url beyond that — whatever you send is what you get back. |
| label | string (optional) | A human-readable label for the link. Up to 100 characters. null when none was given. |
| user_tag | uuid | The opaque user tag of the user that added this link. Fixed when the link is created and never changes after. A user session token owns what it adds, so it never sends this. An administrator names the owner with it. |
{
"id": "2f8b47c1-6e05-4d3a-91b7-0ac5e8d24f36",
"table_id": "984c03a9-7a64-4046-a26f-57cc773c7ec5",
"url": "https://example.com/freezer-case-taste-test-2026",
"label": "Consumer taste-test writeup",
"user_tag": "6f2bd2cb-3ca5-47bd-a680-64cd7cd11312"
}Creating a link
POST https://api.cxipgroup.com/tables/:table_id/table_links
User token required
To attach a link to a table, send an authenticated request to the above endpoint with a url and an optional label.
Every link has an owner. Sent with a user session token, the link is owned by that session's user tag and a user_tag in the body is ignored. Sent with an administrator token, which is bound to nobody, the owner is named with user_tag, and that tag must be one of your own user tags. The rule is the same on tables and documents, and is explained once in the Authenticating guide.
You can attach a link to any table, including one owned by a different user tag.
Parameters:
| Parameter | Value | Description |
|---|---|---|
| table_id | uuid (required) | The unique identifier of the table to attach the link to. |
| url | string (required) | The url. Up to 2048 characters. |
| label | string (optional) | A human-readable label for the link. Up to 100 characters. |
| user_tag | uuid (admin only) | The user tag that will own the link. Required when you send an administrator token, ignored when you send a user session token. |
Request:
curl -i "https://api.cxipgroup.com/tables/984c03a9-7a64-4046-a26f-57cc773c7ec5/table_links" \
-X POST \
-H "Authorization: Bearer ustk_b3d9f0a2-1c47-4e8a-9f65-0d2b8c7e3a41" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/freezer-case-taste-test-2026",
"label": "Consumer taste-test writeup"
}'const response = await fetch("https://api.cxipgroup.com/tables/984c03a9-7a64-4046-a26f-57cc773c7ec5/table_links", {
method: "POST",
body: JSON.stringify({
url: "https://example.com/freezer-case-taste-test-2026",
label: "Consumer taste-test writeup"
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Bearer ustk_b3d9f0a2-1c47-4e8a-9f65-0d2b8c7e3a41"
},
});Response:
{
"id": "2f8b47c1-6e05-4d3a-91b7-0ac5e8d24f36",
"table_id": "984c03a9-7a64-4046-a26f-57cc773c7ec5",
"url": "https://example.com/freezer-case-taste-test-2026",
"label": "Consumer taste-test writeup",
"user_tag": "6f2bd2cb-3ca5-47bd-a680-64cd7cd11312"
}Status codes:
| Status | When |
|---|---|
201 Created | The link was created. Its URL is in the Location header. |
401 Unauthorized | Missing or invalid token. |
404 Not Found | No table with that table_id. |
422 Unprocessable Content | Validation failed. A missing url, or a url or label past its length cap, lands here. So does an administrator token sending no user_tag, or one naming a tag that isn't yours. |
Listing a table's links
GET https://api.cxipgroup.com/tables/:table_id/table_links
User token required
To list the links on a table, send an authenticated request to the above endpoint. This endpoint is paginated — the response is a bare array, with pagination metadata in the response headers.
Links come back in the order they were added, oldest first. Reads are not owner-scoped: the response carries every link on the table, whichever user tag added it. Read the user_tag on each link to tell them apart.
Parameters:
| Parameter | Value | Description |
|---|---|---|
| table_id | uuid (required) | The unique identifier of the table whose links you want. |
| limit | integer (optional) | The number of records to return per page. Defaults to 20, with a maximum of 100. |
| page | integer (optional) | The page of records you want returned. 0 or 1 returns the first page. A value beyond the total number of pages returns nothing. |
Request:
curl -i -G "https://api.cxipgroup.com/tables/984c03a9-7a64-4046-a26f-57cc773c7ec5/table_links" \
-H "Authorization: Bearer ustk_b3d9f0a2-1c47-4e8a-9f65-0d2b8c7e3a41" \
--data-urlencode "limit=20"const response = await fetch("https://api.cxipgroup.com/tables/984c03a9-7a64-4046-a26f-57cc773c7ec5/table_links?limit=20", {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Bearer ustk_b3d9f0a2-1c47-4e8a-9f65-0d2b8c7e3a41"
},
});Response:
[
{
"id": "2f8b47c1-6e05-4d3a-91b7-0ac5e8d24f36",
"table_id": "984c03a9-7a64-4046-a26f-57cc773c7ec5",
"url": "https://example.com/freezer-case-taste-test-2026",
"label": "Consumer taste-test writeup",
"user_tag": "6f2bd2cb-3ca5-47bd-a680-64cd7cd11312"
},
{
"id": "7d1e5a03-b284-4c9f-8e60-3fa17c9b0d52",
"table_id": "984c03a9-7a64-4046-a26f-57cc773c7ec5",
"url": "https://example.com/pint-price-index",
"label": null,
"user_tag": "c41a7e39-8d52-4b06-9f18-2e7bd05a6c83"
}
// ...
]Status codes:
| Status | When |
|---|---|
200 OK | The body is a bare array of the table's links. |
401 Unauthorized | Missing or invalid token. |
404 Not Found | No table with that table_id. |
422 Unprocessable Content | A negative limit. Other unusable pagination values resolve rather than failing. |
Getting a link
GET https://api.cxipgroup.com/tables/:table_id/table_links/:id
User token required
To retrieve a single link, send an authenticated request to the above endpoint. Reads are not owner-scoped — any valid token can retrieve any link, and the link's own owner gets no privileged view of it.
The link is looked up within the table, so a link id that exists but belongs to a different table is a 404 here rather than a 200 for the wrong table's link.
Parameters:
| Parameter | Value | Description |
|---|---|---|
| table_id | uuid (required) | The unique identifier of the table the link is attached to. |
| id | uuid (required) | The unique identifier of the link to retrieve. |
Request:
curl -i "https://api.cxipgroup.com/tables/984c03a9-7a64-4046-a26f-57cc773c7ec5/table_links/2f8b47c1-6e05-4d3a-91b7-0ac5e8d24f36" \
-H "Authorization: Bearer ustk_b3d9f0a2-1c47-4e8a-9f65-0d2b8c7e3a41"const response = await fetch("https://api.cxipgroup.com/tables/984c03a9-7a64-4046-a26f-57cc773c7ec5/table_links/2f8b47c1-6e05-4d3a-91b7-0ac5e8d24f36", {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Bearer ustk_b3d9f0a2-1c47-4e8a-9f65-0d2b8c7e3a41"
},
});Response:
{
"id": "2f8b47c1-6e05-4d3a-91b7-0ac5e8d24f36",
"table_id": "984c03a9-7a64-4046-a26f-57cc773c7ec5",
"url": "https://example.com/freezer-case-taste-test-2026",
"label": "Consumer taste-test writeup",
"user_tag": "6f2bd2cb-3ca5-47bd-a680-64cd7cd11312"
}Status codes:
| Status | When |
|---|---|
200 OK | The body is the link. |
401 Unauthorized | Missing or invalid token. |
404 Not Found | No table with that table_id, or no link with that id on it. |
Deleting a link
DELETE https://api.cxipgroup.com/tables/:table_id/table_links/:id
User token required
To delete a link, send an authenticated request to the above endpoint. A successful delete returns an empty body.
Deletion is scoped to the link's own owner, not the table's. A user session token's tag must be the tag that added the link — the table's owner has no say over links other users attached to their table, and gets a 403 for trying. An administrator token acts by id, and may name a user_tag alongside only if that tag is the link's owner.
A table's links are deleted along with the table itself, so deleting a table needs no cleanup pass over its links first.
Parameters:
| Parameter | Value | Description |
|---|---|---|
| table_id | uuid (required) | The unique identifier of the table the link is attached to. |
| id | uuid (required) | The unique identifier of the link to delete. |
Request:
curl -i "https://api.cxipgroup.com/tables/984c03a9-7a64-4046-a26f-57cc773c7ec5/table_links/2f8b47c1-6e05-4d3a-91b7-0ac5e8d24f36" \
-X DELETE \
-H "Authorization: Bearer ustk_b3d9f0a2-1c47-4e8a-9f65-0d2b8c7e3a41"const response = await fetch("https://api.cxipgroup.com/tables/984c03a9-7a64-4046-a26f-57cc773c7ec5/table_links/2f8b47c1-6e05-4d3a-91b7-0ac5e8d24f36", {
method: "DELETE",
headers: {
"Authorization": "Bearer ustk_b3d9f0a2-1c47-4e8a-9f65-0d2b8c7e3a41"
},
});Status codes:
| Status | When |
|---|---|
204 No Content | The link was deleted. The body is empty. |
401 Unauthorized | Missing or invalid token. |
403 Forbidden | The link was added by a different user tag. |
404 Not Found | No table with that table_id, or no link with that id on it. |