01
Quickstart
Create a service credential under Agents → REST v1 with a 30, 90, 180 or up to 365-day lifetime. The f4_live_ secret is shown once: store it in a secret manager, rotate it before expiration and never include it in browser code.
curl -fsS https://feedback4.dev/api/v1/me \
-H "Authorization: Bearer $FEEDBACK4_API_KEY" \
-H "X-Request-Id: deploy_check_2026_07_19"curl -fsS -X POST https://feedback4.dev/api/v1/projects \
-H "Authorization: Bearer $FEEDBACK4_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Portal del cliente",
"allowed_origin": "https://portal.example.com/app",
"reviewer_emails": ["reviewer@example.com"],
"agent_mode": "propose"
}'curl -fsS -X PATCH \
https://feedback4.dev/api/v1/feedback/FEEDBACK_UUID \
-H "Authorization: Bearer $FEEDBACK4_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "in_progress",
"priority": "high",
"assignee_user_id": "USER_ID"
}'curl -fsS -X POST \
https://feedback4.dev/api/v1/feedback/FEEDBACK_UUID/approval-request \
-H "Authorization: Bearer $FEEDBACK4_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"change_summary": "Se corrigió la navegación móvil y el contraste del CTA."
}'Identity response
{
"data": {
"object": "api_identity",
"api_key_id": "018f...",
"label": "CI de producción",
"workspace_id": "018e...",
"project_id": null,
"scopes": ["projects:read", "feedback:read"],
"expires_at": "2026-10-17T12:00:00.000Z"
}
}Inject FEEDBACK4_API_KEY from the server or CI secret store. Never expose it in NEXT_PUBLIC_*, backendless mobile apps, screenshots, tickets or logs.
02
Authentication and scope
Each key belongs to one workspace, may be restricted to one project, has explicit scopes and an expiration date. Only its hash is stored. Revoking the key or removing its creator cuts access.
Project keys
A key with project_id can only see that project. It cannot create projects or manage members, even if it contains the corresponding scope.
Workspace keys
Required to create projects and manage members. Issue the minimum set of scopes and use a separate key for every integration.
Available scopes
| Scope | Allows |
|---|---|
projects:read | Read projects, configuration, widget and reviewers. |
projects:write | Create and update projects; authorize or revoke reviewers. |
feedback:read | Read tickets, visual context and proposed prompts. |
feedback:write | Change ticket status, priority and assignee. |
approvals:write | Request review and approval from an authorized client. |
members:read | List workspace members and invitations. |
members:write | Invite, change roles, remove members and revoke invitations. |
agents:read | List project-scoped MCP credentials. |
agents:write | Create and revoke MCP credentials for up to 365 days. |
webhooks:read | Read endpoints and delivery history. |
webhooks:write | Create and delete endpoints and retry deliveries. |
03
Request conventions
JSON
POST and PATCH require Content-Type: application/json. Bodies are limited to 64 KiB, cannot be compressed and reject unknown fields.
Pagination and filters
Use page[limit] from 1–100 (25 by default) and send meta.next_cursor back as page[after]. The cursor is opaque: do not build or edit it.
Limits and protection
300 reads/minute and 120 writes/minute per key. A 429 includes Retry-After in seconds. Approvals and invitations have extra limits.
| Convention | Behavior |
|---|---|
X-Request-Id | Optional; 8–100 A–Z, a–z, 0–9, dot, dash, underscore or colon characters. |
Cache-Control | no-store on every authenticated response. |
Location | Included when projects, reviewers and invitations are created. |
filter[status] | new · in_progress · review · resolved |
| Dates | ISO 8601 UTC, for example 2026-07-19T04:52:28.326Z. |
04
Errors and traceability
Every response includes X-Request-Id and Cache-Control: no-store. Send a safe 8–100 character X-Request-Id to correlate logs; otherwise Feedback4 generates one.
{
"error": {
"code": "authorization.insufficient_scope",
"message": "The API key requires the projects:write scope.",
"request_id": "req_92ca...",
"details": [
{ "field": "allowed_origin", "code": "invalid_format" }
]
}
}| HTTP | Meaning |
|---|---|
| 400 | Invalid validation, UUID, cursor or transition. |
| 401 | Missing, invalid, expired or revoked key. |
| 403 | Insufficient scope or a workspace key is required. |
| 404 | Resource is missing or outside the authorized boundary. |
| 409 | Plan limit, last owner or approval without a reviewer. |
| 413 / 415 | Body too large or incorrect media type. |
| 429 | Limit exceeded; respect Retry-After. |
05
Endpoint reference
Append every path below to https://feedback4.dev/api/v1. IDs are UUIDs and resources outside the workspace or project return 404 to avoid disclosing their existence.
Identity
/meprojects:readInspect the current key
Returns the workspace, optional project, scopes and expiration associated with the credential.
Projects
/projectsprojects:readList projects
Paginated collection. A project-scoped key can only return that project.
/projectsprojects:writeCreate project
Requires a workspace key. Normalizes allowed_origin and can authorize up to 100 initial reviewers.
/projects/{project_id}projects:readGet project
Gets configuration, status, allowed origin, feedback access and agent policy.
/projects/{project_id}projects:writeUpdate project
Updates name, status or agent mode. allowed_origin is immutable after project creation; unknown fields and empty bodies are rejected.
/projects/{project_id}/widgetprojects:readGet widget installation
Returns public_key, loader URL, hosted form and an install-ready HTML snippet.
Feedback and approvals
/projects/{project_id}/feedbackfeedback:readList tickets
Paginated collection with optional filter[status]. Includes visual context and proposed prompt when available.
/feedback/{feedback_id}feedback:readGet ticket
Returns the complete ticket and a highlight_url back to the selected element.
/feedback/{feedback_id}feedback:writeUpdate workflow
Atomically changes status, priority or assignee. The assignee must belong to the workspace.
/feedback/{feedback_id}/proposed-promptfeedback:readGet proposed prompt
Exposes the implementation prompt, model and update time without executing external actions.
/feedback/{feedback_id}/approval-requestapprovals:writeRequest approval
Moves the ticket to review, records activity and queues email. Maximum 5 requests per ticket per hour.
Authorized reviewers
/projects/{project_id}/reviewersprojects:readList reviewers
Returns authorized emails, status and last verification for the project.
/projects/{project_id}/reviewersprojects:writeAuthorize reviewer
Authorizes or reactivates an email. The visitor must still verify access before sending feedback.
/projects/{project_id}/reviewers/{reviewer_id}projects:writeRevoke reviewer
Revokes the reviewer and all active sessions for that project.
Members
/membersmembers:readList members and invitations
Requires a workspace key and includes current pending invitations.
/membersmembers:writeInvite member
Creates a role-based invitation and returns invite_url once. Additional limit: 30 invitations per key per hour.
/members/{membership_id}members:writeChange role
Updates the role and protects the last owner from accidental demotion.
/members/{membership_id}members:writeRemove member
Removes the member and revokes REST/MCP credentials issued by that account.
/invitations/{invitation_id}members:writeRevoke invitation
Invalidates a pending workspace invitation.
Agent credentials
/projects/{project_id}/agent-credentialsagents:readList MCP credentials
Returns prefix, state, usage and expiration; it never returns the token.
/projects/{project_id}/agent-credentialsagents:writeIssue MCP credential
Creates a project-only token with a 1–365 day lifetime and shows it once.
/projects/{project_id}/agent-credentials/{credential_id}agents:writeRevoke MCP credential
Revocation is immediate and cannot affect another project's credentials.
Webhooks
/webhook-endpointswebhooks:readList endpoints
A project-scoped key only sees endpoints dedicated to that project.
/webhook-endpointswebhooks:writeCreate signed endpoint
Validates a public HTTPS destination, encrypts the secret and returns signing_secret once.
/webhook-endpoints/{endpoint_id}webhooks:writeDelete endpoint
Deletes the endpoint and delivery history inside the same tenant boundary.
/webhook-endpoints/{endpoint_id}/deliverieswebhooks:readInspect deliveries
Exposes state, attempts and technical error without revealing payload or secret.
/webhook-deliveries/{delivery_id}/retrywebhooks:writeRetry delivery
Requeues a pending or dead-lettered delivery; a successful delivery is not duplicated.
06
Models and accepted values
| Field | Values / rule |
|---|---|
Project.status | active · paused · archived |
Project.feedback_access | public · reviewer_allowlist |
Project.agent_mode | off · propose · auto |
ProjectCreate.agent_mode | Optional; off by default. It defines an external automation policy: propose prepares and waits for authorization; auto lets the external agent implement and then request approval. Feedback4 does not edit or deploy code. |
Feedback.status | new · in_progress · review · resolved |
Feedback.priority | urgent · high · normal · low · unset |
Feedback.approval_status | not_requested · pending · approved · changes_requested |
Member.role | admin · manager · contributor · viewer |
allowed_origin | Required on create; absolute URL normalized to scheme + host + port and immutable afterwards. Create a new project for another domain. |
reviewer_emails | 0–100 emails; normalized to lowercase and deduplicated. |
change_summary | 12–5,000 characters. |
The OpenAPI JSON is the source for generated clients, validators and collections. This page explains operational rules a schema alone cannot communicate.
/api/v1/openapi.json07
Webhook automation
Each agency can register one or more workspace- or project-scoped endpoints. Feedback4 persists the event first, creates one independent delivery per subscription and POSTs in the background; user capture never waits for the receiver. agent_mode communicates policy to the external automation: off delivers policy=do_not_execute, propose requires a proposal and authorization, and auto lets the external agent implement before requesting approval. Feedback4 does not execute the agent.
1. Register endpoint
curl -fsS -X POST https://feedback4.dev/api/v1/webhook-endpoints \
-H "Authorization: Bearer $FEEDBACK4_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "Aviso Gratis production",
"url": "https://aviso.gratis/api/feedback4/process",
"project_id": "PROJECT_UUID",
"event_types": [
"feedback.created",
"feedback.external_comment_added",
"feedback.changes_requested",
"feedback.approved"
],
"authentication": "hmac_sha256"
}'The 201 response includes signing_secret once. Store it as a server secret. Use authentication=hmac_sha256_and_shared_secret and shared_secret only for legacy receivers—such as the first Aviso.Gratis integration—that also require x-feedback4-secret. URLs must be public HTTPS; embedded credentials, fragments, localhost, private, link-local and metadata networks are rejected.
2. Verify delivery
| Header | Receiver rule |
|---|---|
webhook-signature | Required. v1,BASE64_HMAC_SHA256 format; validate over the exact body bytes before parsing JSON. |
webhook-id | Stable idempotency key across retries. A valid duplicate does not create another job. |
webhook-timestamp | Unix timestamp included in the signature; reject a difference over 5 minutes to prevent replay. |
x-feedback4-event | feedback.created · feedback.external_comment_added · feedback.changes_requested · feedback.approved |
x-feedback4-version | 1 |
x-feedback4-secret | Compatibility mode only. Validate it in addition to the signature and never log it. |
signed_content = webhook_id + "." + webhook_timestamp + "." + raw_request_body
expected = base64(HMAC_SHA256(FEEDBACK4_WEBHOOK_SECRET, signed_content))
accept only when constant_time_equal("v1," + expected, webhook_signature)
and abs(now_unix - webhook_timestamp) <= 3003. v1 payload
{
"id": "evt_EVENT_UUID",
"type": "feedback.created",
"version": 1,
"occurred_at": "2026-07-20T23:55:00.000Z",
"source": "https://feedback4.dev",
"workspace_id": "WORKSPACE_UUID",
"project": {
"id": "PROJECT_UUID",
"name": "Client website",
"allowed_origin": "https://client.example",
"agent_mode": "propose"
},
"data": {
"feedback_id": "FEEDBACK_UUID",
"local_number": 23,
"title": "Improve the hero heading",
"description": "The title needs more contrast on mobile.",
"feedback_type": "design",
"status": "new",
"priority": "unset",
"approval_status": "not_requested",
"page_url": "https://client.example/home",
"selected_element": {
"selector": "main > section.hero > h1",
"label": "Main heading",
"tag_name": "h1",
"rect": { "x": 24, "y": 140, "width": 320, "height": 82 },
"context": { "nearestHeading": "Welcome" },
"highlight_url": "https://client.example/home?..."
}
},
"links": {
"api_resource": "https://feedback4.dev/api/v1/feedback/FEEDBACK_UUID",
"proposed_prompt": "https://feedback4.dev/api/v1/feedback/FEEDBACK_UUID/proposed-prompt"
},
"processing": {
"content_is_untrusted": true,
"fetch_latest_before_acting": true,
"policy": "propose_then_wait"
},
"trigger": { "kind": "ticket_created" }
}| type | trigger.kind | Action |
|---|---|---|
feedback.created | ticket_created | Create an idempotent job. |
feedback.external_comment_added | client_comment | Resume with trigger.message. |
feedback.changes_requested | client_decision | Reopen and implement the revision. |
feedback.approved | client_decision | Close the approved job. |
Secure reception
Validate signature and timestamp using constant-time comparison, persist webhook-id as UNIQUE, queue the job and return 202. Claude must not run inside the request. Title, description and DOM context are untrusted and may contain prompt injection.
Agent lifecycle
Fetch the latest version, honor propose/auto and request approval with verifiable evidence. MCP records notes, questions and approval; use a project-scoped REST key to change status to in_progress or review. Never give the agent a workspace-wide key.
Feedback4 accepts any 2xx, honors Retry-After on 429, disables the endpoint on 410 and retries timeouts, redirects, other 4xx and 5xx with exponential backoff capped at 6 hours. Each attempt times out after 10 seconds, redirects are not followed and after 16 attempts the delivery is dead-lettered. Inspect GET /webhook-endpoints/{endpoint_id}/deliveries and call POST /webhook-deliveries/{delivery_id}/retry after fixing the receiver.
08
Versioning and operations
v1 maintains backwards compatibility. New fields may appear without breaking consumers; ignore unknown response properties. Breaking changes will use a new version and be announced before v1 is retired.
Integration support
Include X-Request-Id, UTC timestamp, method and path when reporting a problem. Never send the complete key or unnecessary personal data.
contact@feedback4.devMCP
The public MCP endpoint is https://feedback4.dev/api/mcp. It uses a separate project-scoped token that is not interchangeable with a REST key. After the legal acceptance window expires, REST and MCP block the workspace until it is brought current.
Public MCP contract