Frames
A frame defines the axis of evaluation used by positions on a table. It carries four values, each one with a label — they could be Dialectic, Diatribic, Didactic, Diabolical — and, for each of those four, the question a person answers and a word at each end of the scale they answer it on. Where a frame carries that text it arrives with the frame, so there is no wording for a client to hold its own copy of. Not every frame carries it, and the frame object says what a client sees when one doesn't.
Frames are immutable. There is no update endpoint: correcting one means deleting it and creating its replacement, which is only possible while no table is built on it. A frame's labels, questions, and end labels are the whole of what gives a plotted position its meaning, and one frame is shared by every table built on it, so an edit would change how positions on other people's tables read without moving a single coordinate.
That is what makes a frame worth holding on to. Fetch the collection once and match tables against it locally by their frame_id, rather than fetching a frame per table — a frame you already hold cannot have become wrong, only missing, and the next fetch resolves that.
The frame object
| Attribute | Type | Description |
|---|---|---|
| id | uuid | The unique identifier of the frame. |
| name | string | Name of the frame. Required, and must be unique across all frames. |
| val1 | string | The first value's label. Required. Up to 50 characters. |
| val2 | string | The second value's label. Required. Up to 50 characters. |
| val3 | string | The third value's label. Required. Up to 50 characters. |
| val4 | string | The fourth value's label. Required. Up to 50 characters. |
| val1_question | string | The question answered for the first value. Up to 255 characters. |
| val1_min_label | string | The label at the low end of the first value's scale. Up to 50 characters. |
| val1_max_label | string | The label at the high end of the first value's scale. Up to 50 characters. |
| val2_question | string | The question answered for the second value. Up to 255 characters. |
| val2_min_label | string | The label at the low end of the second value's scale. Up to 50 characters. |
| val2_max_label | string | The label at the high end of the second value's scale. Up to 50 characters. |
| val3_question | string | The question answered for the third value. Up to 255 characters. |
| val3_min_label | string | The label at the low end of the third value's scale. Up to 50 characters. |
| val3_max_label | string | The label at the high end of the third value's scale. Up to 50 characters. |
| val4_question | string | The question answered for the fourth value. Up to 255 characters. |
| val4_min_label | string | The label at the low end of the fourth value's scale. Up to 50 characters. |
| val4_max_label | string | The label at the high end of the fourth value's scale. Up to 50 characters. |
{
"id": "9651ded6-b2d8-451e-b4ac-3bce40c9201c",
"name": "Rhetorical",
"val1": "Dialectic",
"val2": "Diatribic",
"val3": "Didactic",
"val4": "Diabolical",
"val1_question": "How well does this hold up under argument?",
"val1_min_label": "Falls apart",
"val1_max_label": "Holds firm",
"val2_question": "How strongly does this provoke you?",
"val2_min_label": "Barely",
"val2_max_label": "Fiercely",
"val3_question": "How much does this teach you?",
"val3_min_label": "Nothing new",
"val3_max_label": "Genuinely instructive",
"val4_question": "How mischievous is this?",
"val4_min_label": "Wholesome",
"val4_max_label": "Fiendish"
}A value's question and its two end labels arrive together or not at all: a frame carries the question and both end labels for all four of its values, or carries none of the twelve. There is no partial set, so a client meets one of exactly two shapes.
These twelve are where a frame's presentation text lives — the question to put in front of a person, and the words to label each end of the scale they answer on. A frame created before the fields existed has all twelve null, and no request will fill them in: a frame is never edited, so there is no endpoint that adds this text to a frame that came without it. So read them with a fallback rather than an assumption. Every frame created through the API carries all twelve, which is a statement about new frames rather than a guarantee about the one in front of you.
The question's voice belongs to the frame. One frame may ask a person about themselves — how important is this to you? — where another asks them to place a subject. Within a single frame all four questions address the same subject in the same voice, so the four read as one coherent set.
Creating a frame
POST https://api.cxipgroup.com/frames
Admin token required
To create a frame, send an authenticated request to the above endpoint with the frame's attributes in the body. Every parameter below is required: a frame cannot be created without the question and end labels for each of its four values, and an empty string is refused the same way a missing field is.
Parameters:
| Parameter | Value | Description |
|---|---|---|
| name | string (required) | The frame's name. Must be unique across all frames. |
| val1 | string (required) | The first value's label. Up to 50 characters. |
| val2 | string (required) | The second value's label. Up to 50 characters. |
| val3 | string (required) | The third value's label. Up to 50 characters. |
| val4 | string (required) | The fourth value's label. Up to 50 characters. |
| val1_question | string (required) | The question answered for the first value. Up to 255 characters. |
| val1_min_label | string (required) | The label at the low end of the first value's scale. Up to 50 characters. |
| val1_max_label | string (required) | The label at the high end of the first value's scale. Up to 50 characters. |
| val2_question | string (required) | The question answered for the second value. Up to 255 characters. |
| val2_min_label | string (required) | The label at the low end of the second value's scale. Up to 50 characters. |
| val2_max_label | string (required) | The label at the high end of the second value's scale. Up to 50 characters. |
| val3_question | string (required) | The question answered for the third value. Up to 255 characters. |
| val3_min_label | string (required) | The label at the low end of the third value's scale. Up to 50 characters. |
| val3_max_label | string (required) | The label at the high end of the third value's scale. Up to 50 characters. |
| val4_question | string (required) | The question answered for the fourth value. Up to 255 characters. |
| val4_min_label | string (required) | The label at the low end of the fourth value's scale. Up to 50 characters. |
| val4_max_label | string (required) | The label at the high end of the fourth value's scale. Up to 50 characters. |
Request:
curl -i "https://api.cxipgroup.com/frames" \
-X POST \
-H "Authorization: Bearer adtk_80573e65-c815-49d8-b7b9-75cf2f143876" \
-H "Content-Type: application/json" \
-d '{
"name": "Deliberation",
"val1": "Dialectic",
"val2": "Wellness",
"val3": "Reasoning",
"val4": "Desirability",
"val1_question": "How well does this hold up under argument?",
"val1_min_label": "Falls apart",
"val1_max_label": "Holds firm",
"val2_question": "How much does this affect your wellbeing?",
"val2_min_label": "No impact",
"val2_max_label": "Strong impact",
"val3_question": "How much have you thought about this?",
"val3_min_label": "Quick reaction",
"val3_max_label": "Well reasoned",
"val4_question": "How much do you want this?",
"val4_min_label": "Not at all",
"val4_max_label": "Very much"
}'const response = await fetch("https://api.cxipgroup.com/frames", {
method: "POST",
body: JSON.stringify({
name: "Deliberation",
val1: "Dialectic",
val2: "Wellness",
val3: "Reasoning",
val4: "Desirability",
val1_question: "How well does this hold up under argument?",
val1_min_label: "Falls apart",
val1_max_label: "Holds firm",
val2_question: "How much does this affect your wellbeing?",
val2_min_label: "No impact",
val2_max_label: "Strong impact",
val3_question: "How much have you thought about this?",
val3_min_label: "Quick reaction",
val3_max_label: "Well reasoned",
val4_question: "How much do you want this?",
val4_min_label: "Not at all",
val4_max_label: "Very much"
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Bearer adtk_80573e65-c815-49d8-b7b9-75cf2f143876"
},
});Response:
{
"id": "b9cdcd91-8fe8-41dc-9b46-ae6eb5e3c751",
"name": "Deliberation",
"val1": "Dialectic",
"val2": "Wellness",
"val3": "Reasoning",
"val4": "Desirability",
"val1_question": "How well does this hold up under argument?",
"val1_min_label": "Falls apart",
"val1_max_label": "Holds firm",
"val2_question": "How much does this affect your wellbeing?",
"val2_min_label": "No impact",
"val2_max_label": "Strong impact",
"val3_question": "How much have you thought about this?",
"val3_min_label": "Quick reaction",
"val3_max_label": "Well reasoned",
"val4_question": "How much do you want this?",
"val4_min_label": "Not at all",
"val4_max_label": "Very much"
}Status codes:
| Status | When |
|---|---|
201 Created | The frame was created. Its URL is in the Location header. |
401 Unauthorized | Missing or invalid token. |
403 Forbidden | The token is not an admin (adtk_) token. |
422 Unprocessable Content | Validation failed. A name already in use lands here, as does a missing, blank, or over-long question or end label. The response body names the fields at fault. |
Listing frames
GET https://api.cxipgroup.com/frames
User token required
To list frames, 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.
Frames come back oldest first by created_at, with id settling any tie. The order is total, so paging the whole collection reaches every frame exactly once, and a frame created later goes to the end rather than displacing what is already there.
Parameters:
| Parameter | Value | Description |
|---|---|---|
| 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. |
Request:
curl -i -G "https://api.cxipgroup.com/frames" \
-H "Authorization: Bearer adtk_80573e65-c815-49d8-b7b9-75cf2f143876" \
--data-urlencode "limit=100" \
--data-urlencode "page=1"const response = await fetch("https://api.cxipgroup.com/frames?limit=100&page=1", {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Bearer adtk_80573e65-c815-49d8-b7b9-75cf2f143876"
},
});Response:
[
{
"id": "3fa26bc2-6a32-4655-be8d-1fc2cac1291a",
"name": "Rhetorical",
"val1": "Dialectic",
"val2": "Diatribic",
"val3": "Didactic",
"val4": "Diabolical",
"val1_question": "How well does this hold up under argument?",
"val1_min_label": "Falls apart",
"val1_max_label": "Holds firm",
"val2_question": "How strongly does this provoke you?",
"val2_min_label": "Barely",
"val2_max_label": "Fiercely",
"val3_question": "How much does this teach you?",
"val3_min_label": "Nothing new",
"val3_max_label": "Genuinely instructive",
"val4_question": "How mischievous is this?",
"val4_min_label": "Wholesome",
"val4_max_label": "Fiendish"
},
{
"id": "4fc443fc-5c06-42a7-8e09-e649b4b388d8",
"name": "Kawaii",
"val1": "Delectable",
"val2": "Cute",
"val3": "Clever",
"val4": "Gross",
"val1_question": "How much do you want to eat this?",
"val1_min_label": "Not a bite",
"val1_max_label": "All of it",
"val2_question": "How cute is this?",
"val2_min_label": "Plain",
"val2_max_label": "Unbearably cute",
"val3_question": "How clever is this?",
"val3_min_label": "Obvious",
"val3_max_label": "Ingenious",
"val4_question": "How much does this put you off?",
"val4_min_label": "Not at all",
"val4_max_label": "Completely"
}
]Status codes:
| Status | When |
|---|---|
200 OK | The body is a bare array of frames. |
401 Unauthorized | Missing or invalid token. |
422 Unprocessable Content | A negative limit. Other unusable pagination values resolve rather than failing. |
Getting a frame
GET https://api.cxipgroup.com/frames/:id
User token required
To retrieve a single frame by its id, send an authenticated request to the above endpoint.
Parameters:
| Parameter | Value | Description |
|---|---|---|
| id | uuid (required) | The unique identifier of the frame to retrieve. |
Request:
curl -i "https://api.cxipgroup.com/frames/9651ded6-b2d8-451e-b4ac-3bce40c9201c" \
-H "Authorization: Bearer adtk_80573e65-c815-49d8-b7b9-75cf2f143876"const response = await fetch("https://api.cxipgroup.com/frames/9651ded6-b2d8-451e-b4ac-3bce40c9201c", {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Bearer adtk_80573e65-c815-49d8-b7b9-75cf2f143876"
},
});Response:
{
"id": "9651ded6-b2d8-451e-b4ac-3bce40c9201c",
"name": "Rhetorical",
"val1": "Dialectic",
"val2": "Diatribic",
"val3": "Didactic",
"val4": "Diabolical",
"val1_question": "How well does this hold up under argument?",
"val1_min_label": "Falls apart",
"val1_max_label": "Holds firm",
"val2_question": "How strongly does this provoke you?",
"val2_min_label": "Barely",
"val2_max_label": "Fiercely",
"val3_question": "How much does this teach you?",
"val3_min_label": "Nothing new",
"val3_max_label": "Genuinely instructive",
"val4_question": "How mischievous is this?",
"val4_min_label": "Wholesome",
"val4_max_label": "Fiendish"
}Status codes:
| Status | When |
|---|---|
200 OK | The body is the frame. |
401 Unauthorized | Missing or invalid token. |
404 Not Found | No frame with that id. |
Deleting a frame
DELETE https://api.cxipgroup.com/frames/:id
Admin token required
To delete a frame, send an authenticated request to the above endpoint. A successful delete returns an empty body.
Because frames are immutable, this is also how you correct one: delete it and create its replacement, which carries a new id. It only works while nothing is built on the frame — a frame any table points at is a 409 for as long as that table exists, so a frame already in use has no correction path.
Parameters:
| Parameter | Value | Description |
|---|---|---|
| id | uuid (required) | The unique identifier of the frame to delete. |
Request:
curl -i "https://api.cxipgroup.com/frames/9651ded6-b2d8-451e-b4ac-3bce40c9201c" \
-X DELETE \
-H "Authorization: Bearer adtk_80573e65-c815-49d8-b7b9-75cf2f143876"const response = await fetch("https://api.cxipgroup.com/frames/9651ded6-b2d8-451e-b4ac-3bce40c9201c", {
method: "DELETE",
headers: {
"Authorization": "Bearer adtk_80573e65-c815-49d8-b7b9-75cf2f143876"
},
});Status codes:
| Status | When |
|---|---|
204 No Content | The frame was deleted. The body is empty. |
401 Unauthorized | Missing or invalid token. |
403 Forbidden | The token is not an admin (adtk_) token. |
404 Not Found | No frame with that id. |
409 Conflict | The frame is still referenced by one or more tables. |