> For the complete documentation index, see [llms.txt](https://boundaryai.gitbook.io/boundaryai-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://boundaryai.gitbook.io/boundaryai-docs/api-and-webhooks/your-data-and-privacy.md).

# Your Data & Privacy

List what you've pushed and erase it again — `POST /feedback/erase` handles GDPR erasure requests by `external_id` or `customer_id` for API-pushed rows.

## List pushed feedback items (cursor-paginated)

> Keyset-paginated listing of items the Input API ingested for this org (never native survey responses). Filter by \`source\_id\` and/or \`field\_id\`; pass the returned \`next\_cursor\` back as \`cursor\` to fetch the next page. Ordered by item id ascending, so pages are stable while new items land.\
> \
> \*\*Response envelope migration:\*\* during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's \`data\` property, without the \`{"data": ...}\` wrapper). The \`Deprecation\` and \`Sunset\` headers on every 2xx announce this envelope migration — they do \*\*not\*\* deprecate the endpoint itself. After the \`Sunset\` date, responses will be wrapped as \`{"data": ...}\` exactly as the schema declares.

```json
{"openapi":"3.1.0","info":{"title":"BAI Analytics API","version":"1.3.0"},"tags":[{"name":"Your data & privacy","description":"List what you've pushed and erase it again — `POST /feedback/erase` handles GDPR erasure requests by `external_id` or `customer_id` for API-pushed rows."}],"servers":[{"url":"https://app.boundary-ai.com","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"BAI Analytics API key","description":"API key authentication: pass your key in the Authorization header as `Bearer inpk_live_...` (or `inpk_test_...` for a test-environment key). Create and manage keys in the dashboard under Integrations Hub -> API Keys. Each key carries a permission scope (push, create, send, mcp_read, or all) and a per-minute rate limit."}},"schemas":{"FeedbackListResponse":{"description":"Cursor page of API-pushed feedback (partner vocabulary).","properties":{"items":{"items":{"$ref":"#/components/schemas/FeedbackItem"},"title":"Items","type":"array"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Pass back as ?cursor= to fetch the next page; null on the last page","title":"Next Cursor"},"has_more":{"default":false,"title":"Has More","type":"boolean"}},"title":"FeedbackListResponse","type":"object"},"FeedbackItem":{"properties":{"id":{"description":"Cursor-orderable item id","title":"Id","type":"integer"},"source_id":{"title":"Source Id","type":"integer"},"field_id":{"title":"Field Id","type":"integer"},"type":{"description":"text | numeric | choice","title":"Type","type":"string"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Text"},"numeric":{"anyOf":[{"type":"number"},{"type":"null"}],"default":null,"title":"Numeric"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"External Id"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"description":"Structured ingest metadata as pushed","title":"Metadata"},"source_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Source Reference"},"occurred_at":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"ISO-8601 event time","title":"Occurred At"}},"required":["id","source_id","field_id","type"],"title":"FeedbackItem","type":"object"},"ErrorEnvelope":{"description":"Top-level error response.\n\nSchema name in the OpenAPI document: ``ErrorEnvelope``.","properties":{"error":{"$ref":"#/components/schemas/ErrorObject"},"details":{"anyOf":[{},{"type":"null"}],"default":null,"description":"Optional structured details (e.g. validation field map)","title":"Details"}},"required":["error"],"title":"ErrorEnvelope","type":"object"},"ErrorObject":{"description":"The inner ``error`` field of an error envelope.\n\nSchema name in the OpenAPI document: ``ErrorObject``.","properties":{"code":{"description":"Machine-readable error code drawn from ErrorCode enum","title":"Code","type":"string"},"message":{"description":"Human-readable error message, safe to display","title":"Message","type":"string"}},"required":["code","message"],"title":"ErrorObject","type":"object"}},"responses":{"RateLimited":{"description":"Rate limit exceeded — see Retry-After header.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/XRateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"headers":{"RetryAfter":{"description":"Seconds to wait before retrying. Emitted on 429 responses and on 503 responses from the global rate limiter. Rate-limit windows are fixed (anchored at the first request in the window): requests rejected with 429 do not extend the window, so waiting until ``X-RateLimit-Reset`` always recovers.","schema":{"type":"integer","minimum":1}},"XRateLimitLimit":{"description":"Total requests allowed in the current rate-limit window.","schema":{"type":"integer","minimum":1}},"XRateLimitRemaining":{"description":"Requests remaining in the current rate-limit window.","schema":{"type":"integer","minimum":0}},"XRateLimitReset":{"description":"Unix timestamp when the rate-limit window resets. The window is fixed from its first request — 429-rejected requests do not push this forward.","schema":{"type":"integer","minimum":0}}}},"paths":{"/api/input/feedback":{"get":{"summary":"List pushed feedback items (cursor-paginated)","description":"Keyset-paginated listing of items the Input API ingested for this org (never native survey responses). Filter by `source_id` and/or `field_id`; pass the returned `next_cursor` back as `cursor` to fetch the next page. Ordered by item id ascending, so pages are stable while new items land.\n\n**Response envelope migration:** during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's `data` property, without the `{\"data\": ...}` wrapper). The `Deprecation` and `Sunset` headers on every 2xx announce this envelope migration — they do **not** deprecate the endpoint itself. After the `Sunset` date, responses will be wrapped as `{\"data\": ...}` exactly as the schema declares.","tags":["Your data & privacy"],"operationId":"inputapi_get_feedback","parameters":[{"name":"source_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"field_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque cursor from the previous page's next_cursor"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FeedbackListResponse"}},"required":["data"]}}}},"400":{"description":"Bad Request — validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized — missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Bulk-erase pushed feedback (erasure requests)

> Deletes items the Input API ingested, selected by \`external\_ids\` (item-level) or \`customer\_id\` (erases every item whose ingest metadata carries that customer — the GDPR erasure shape). Exactly one selector is required; \`feedback\_group\_id\` optionally narrows the scope. Only API-pushed rows are ever touched. Existing analyses are not rewritten retroactively — they drop the erased items on their next run.\
> \
> \*\*Requires permission:\*\* \`push\` or \`create\` or \`all\`.\
> \
> \*\*Response envelope migration:\*\* during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's \`data\` property, without the \`{"data": ...}\` wrapper). The \`Deprecation\` and \`Sunset\` headers on every 2xx announce this envelope migration — they do \*\*not\*\* deprecate the endpoint itself. After the \`Sunset\` date, responses will be wrapped as \`{"data": ...}\` exactly as the schema declares.

```json
{"openapi":"3.1.0","info":{"title":"BAI Analytics API","version":"1.3.0"},"tags":[{"name":"Your data & privacy","description":"List what you've pushed and erase it again — `POST /feedback/erase` handles GDPR erasure requests by `external_id` or `customer_id` for API-pushed rows."}],"servers":[{"url":"https://app.boundary-ai.com","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"BAI Analytics API key","description":"API key authentication: pass your key in the Authorization header as `Bearer inpk_live_...` (or `inpk_test_...` for a test-environment key). Create and manage keys in the dashboard under Integrations Hub -> API Keys. Each key carries a permission scope (push, create, send, mcp_read, or all) and a per-minute rate limit."}},"schemas":{"EraseFeedbackRequest":{"description":"Selector for a bulk erasure. Exactly one of external_ids /\ncustomer_id is required; feedback_group_id optionally narrows scope.","properties":{"external_ids":{"anyOf":[{"items":{"type":"string"},"maxItems":1000,"type":"array"},{"type":"null"}],"default":null,"title":"External Ids"},"customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Erase every item whose ingest metadata carries this customer_id","title":"Customer Id"},"feedback_group_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Feedback Group Id"}},"title":"EraseFeedbackRequest","type":"object"},"EraseFeedbackResponse":{"properties":{"status":{"const":"success","title":"Status","type":"string"},"deleted":{"description":"Answer rows deleted","minimum":0,"title":"Deleted","type":"integer"},"sources_affected":{"minimum":0,"title":"Sources Affected","type":"integer"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Message"}},"required":["status","deleted","sources_affected"],"title":"EraseFeedbackResponse","type":"object"},"ErrorEnvelope":{"description":"Top-level error response.\n\nSchema name in the OpenAPI document: ``ErrorEnvelope``.","properties":{"error":{"$ref":"#/components/schemas/ErrorObject"},"details":{"anyOf":[{},{"type":"null"}],"default":null,"description":"Optional structured details (e.g. validation field map)","title":"Details"}},"required":["error"],"title":"ErrorEnvelope","type":"object"},"ErrorObject":{"description":"The inner ``error`` field of an error envelope.\n\nSchema name in the OpenAPI document: ``ErrorObject``.","properties":{"code":{"description":"Machine-readable error code drawn from ErrorCode enum","title":"Code","type":"string"},"message":{"description":"Human-readable error message, safe to display","title":"Message","type":"string"}},"required":["code","message"],"title":"ErrorObject","type":"object"}},"responses":{"RateLimited":{"description":"Rate limit exceeded — see Retry-After header.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/XRateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"headers":{"RetryAfter":{"description":"Seconds to wait before retrying. Emitted on 429 responses and on 503 responses from the global rate limiter. Rate-limit windows are fixed (anchored at the first request in the window): requests rejected with 429 do not extend the window, so waiting until ``X-RateLimit-Reset`` always recovers.","schema":{"type":"integer","minimum":1}},"XRateLimitLimit":{"description":"Total requests allowed in the current rate-limit window.","schema":{"type":"integer","minimum":1}},"XRateLimitRemaining":{"description":"Requests remaining in the current rate-limit window.","schema":{"type":"integer","minimum":0}},"XRateLimitReset":{"description":"Unix timestamp when the rate-limit window resets. The window is fixed from its first request — 429-rejected requests do not push this forward.","schema":{"type":"integer","minimum":0}}}},"paths":{"/api/input/feedback/erase":{"post":{"summary":"Bulk-erase pushed feedback (erasure requests)","description":"Deletes items the Input API ingested, selected by `external_ids` (item-level) or `customer_id` (erases every item whose ingest metadata carries that customer — the GDPR erasure shape). Exactly one selector is required; `feedback_group_id` optionally narrows the scope. Only API-pushed rows are ever touched. Existing analyses are not rewritten retroactively — they drop the erased items on their next run.\n\n**Requires permission:** `push` or `create` or `all`.\n\n**Response envelope migration:** during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's `data` property, without the `{\"data\": ...}` wrapper). The `Deprecation` and `Sunset` headers on every 2xx announce this envelope migration — they do **not** deprecate the endpoint itself. After the `Sunset` date, responses will be wrapped as `{\"data\": ...}` exactly as the schema declares.","tags":["Your data & privacy"],"operationId":"inputapi_post_feedback_erase","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Opaque retry key (24h window)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EraseFeedbackRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EraseFeedbackResponse"}},"required":["data"]}}}},"400":{"description":"Bad Request — validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized — missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden — insufficient permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```
