POST/api/v1/opinions
Submit an opinion
Create or update a sentiment opinion for an AI model. Requires a Bearer API key.
Example
curl -X POST 'https://vibetracker.app/api/v1/opinions' \
-H 'Authorization: Bearer $VIBETRACKER_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"model": "gpt-4o",
"score": -1,
"updateOptionalContext": true,
"useCase": "coding",
"interface": "api",
"toolId": "openai-api",
"comment": "Regression in tool calls after the latest deploy."
}'Headers
| Field | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Yes | Bearer token: Bearer YOUR_API_KEY |
| Content-Type | string | Yes | Must be application/json |
Request body
Only model and score are always required. Context fields are optional unless you are sending tool information.
| Field | Type | Required | Description |
|---|---|---|---|
| model | string | Yes | Model slug. You can send gpt-4o or the canonical full slug openai/gpt-4o. |
| fullSlug | string | No | Backward-compatible alias for model. Prefer model on new clients. |
| score | -1 | 0 | 1 | Yes | Negative, neutral, or positive sentiment |
| updateOptionalContext | boolean | No | When updating an active opinion, whether to overwrite optional context fields |
| useCase | enum | No | One of the supported use-case values listed below |
| interface | string | Conditional | Required when sending tool context. Must be a valid platform option. |
| toolId | string | Conditional | Must be valid for the selected interface. Use "other" with toolNameOther. |
| toolNameOther | string | Conditional | Only when toolId is "other". Max 80 chars, no links. |
| comment | string | No | Free-text comment. Max 280 chars, no links. |
Supported useCase values
codingwritingresearchbrainstormingcustomer-supportimage-generationdata-analysisschool-studyingResponse body
Returns the stored opinion state in a compact external-facing shape.
| Field | Type | Required | Description |
|---|---|---|---|
| opinionId | string | Always | Stable ID for the created or updated opinion |
| createdNewOpinion | boolean | Always | true if a new opinion was created, false if an active one was updated |
| model | object | Always | Canonical model info: displayName and fullSlug |
| score | -1 | 0 | 1 | Always | The stored sentiment score after the write |
| useCase | string | null | Always | Resolved use-case on the opinion |
| interface | string | null | Always | Resolved interface on the opinion |
| toolId | string | null | Always | Resolved tool identifier |
| toolNameOther | string | null | Always | Custom tool name when toolId is "other" |
| comment | string | null | Always | Stored comment after normalization |
| moderationStatus | "visible" | "pending" | "suppressed" | Always | Moderation state for the opinion |
| submittedAt | number | Always | Unix timestamp (ms) when the opinion was submitted |
| cooldownEndsAt | number | Always | Unix timestamp (ms) when the 30-minute active window ends |
Errors
| Field | Type | Required | Description |
|---|---|---|---|
| 401 | { "error": string } | Auth failure | Invalid, revoked, or expired API key |
| 400 | { "error": string } | Validation failure | Request body validation issues or other submission errors |
Behavior
- If the same user submits again while a prior opinion is still active (within 30 minutes), the existing opinion is updated instead of creating a new one.
modelaccepts either a short slug likegpt-5.4or a canonicalprovider/modelslug. If a short slug is ambiguous, send the canonical full slug.commentandtoolNameOtherreject links and are trimmed before storage.updateOptionalContextcontrols whether optional fields overwrite the context on an existing active opinion.
Rate limiting
Opinions have a 30-minute active window. API-key submissions are also limited by trusted client network.
New to the API? Start with the quickstart.