# Feedback REST API

> `POST https://escutaproduto.com/api/v1/feedback` with a JSON body containing your product `key` and a `message`. A success returns `201` with the new feedback `id`. The full contract is published as OpenAPI 3.1 at `/openapi.json`.

Source: https://escutaproduto.com/docs/api
Last updated: 2026-10-09

## Endpoint

```text
POST https://escutaproduto.com/api/v1/feedback
Content-Type: application/json
```

No API secret is needed: the product key identifies where the feedback goes and can only be used to send feedback, never to read it. The machine-readable spec lives at [/openapi.json](https://escutaproduto.com/openapi.json).

## Example

```bash
curl -X POST https://escutaproduto.com/api/v1/feedback \
  -H "content-type: application/json" \
  -d '{
    "key": "pk_your_product_key",
    "kind": "idea",
    "message": "Let me export invoices as PDF",
    "rating": 4,
    "email": "customer@example.com",
    "metadata": { "plan": "pro", "appVersion": "2.3.1" }
  }'
```

```json
{ "ok": true, "id": "3f0c6a8e-7d1b-4c55-9a43-1f2e8b7c9d10" }
```

## Request body

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `key` | string | yes | The product's public key (`pk_…`). |
| `message` | string | yes | At least 2 characters. Text beyond 5,000 characters is cut off. |
| `kind` | string | no | `bug`, `idea`, `praise` or `other`. Defaults to `other`. |
| `rating` | integer | no | 1 to 5. |
| `name` | string | no | Up to 120 characters (longer values are cut off). |
| `email` | string | no | A valid email address, up to 254 characters. |
| `pageUrl` | string | no | An http(s) URL, up to 2,048 characters. Other values are ignored. |
| `metadata` | object | no | Any JSON object up to 4 KB, such as user id, plan or app version. |

## Responses

| Status | Meaning |
| --- | --- |
| `201` | Saved. Body: `{ "ok": true, "id": "…" }`. |
| `400` | Invalid JSON or a field failed validation. Body: `{ "error": "…" }`. |
| `403` | The request came from a browser origin that isn't in the product's allowed origins. |
| `404` | Unknown product key. |
| `413` | Body larger than 16 KB. |
| `429` | More than 10 submissions per minute from the same IP to the same product. |

## Calling it from a browser

Browsers may call the endpoint directly. It answers CORS preflights, and the widget sends `text/plain` to skip the preflight entirely. If the product has **Allowed origins** configured, the `Origin` header must match one of them. Server-side requests (no `Origin` header) are always accepted.

## Calling it from a backend

```ts
await fetch("https://escutaproduto.com/api/v1/feedback", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    key: process.env.ESCUTA_PRODUTO_KEY,
    kind: "bug",
    message: "Checkout failed with card_declined",
    metadata: { orderId: "ord_123" },
  }),
});
```

## Frequently asked questions

### Do I need an API key or secret to send feedback?

No. Send the product's public key in the request body. It can only create feedback, never read it, so it's safe to ship in apps and websites.

### What is the rate limit of the feedback API?

10 submissions per minute per IP address and product. Requests over the limit get HTTP 429.

### Is there an OpenAPI specification?

Yes. The OpenAPI 3.1 document is published at https://escutaproduto.com/openapi.json and can be imported into Postman, Insomnia or any code generator.
