NisuformDocs

Submissions API

Page through submissions, mark them read, starred or spam, delete them, download uploaded files and export CSV with the Nisuform API.

Every request needs an API key, and the form must be in the key's team. Otherwise the API answers 404.

The submission object

{
  "id": "8d0f5a4e-3b9c-4f7a-9e21-6c4d2b1a0f93",
  "formId": "2f6b7c1e-9a4d-4c3e-8b21-7d5e6f4a3b2c",
  "data": {
    "name": "Jane Doe",
    "email": "jane@example.com",
    "message": "Hello!"
  },
  "meta": {
    "ip": "203.0.113.7",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...",
    "referrer": "https://example.com/contact",
    "country": "DE",
    "deviceClass": "desktop"
  },
  "read": false,
  "starred": false,
  "isSpam": false,
  "isTest": false,
  "createdAt": "2026-09-29T09:29:59.412Z"
}
FieldDescription
idThe submission ID.
formIdThe form it was sent to.
dataThe answers, as field name and text value pairs. Special fields like _gotcha are never stored.
meta.ipThe visitor's IP address. Left out when the form doesn't store IP addresses.
meta.userAgentThe visitor's browser.
meta.referrerThe page the form was sent from, or an empty string.
meta.countryTwo-letter country code, when known.
meta.deviceClassmobile, desktop, tablet or bot, when known.
meta.filesUploaded files, each with name, mime and size in bytes. Only present when files were attached.
readWhether it was opened or marked read.
starredWhether it is starred.
isSpamWhether it is in the spam folder.
isTestWhether it is a test submission sent from the dashboard or the API.
createdAtWhen it was received.

When answers don't pass your form's field checks, the submission is still stored, arrives unread and has an extra data._invalid value: a JSON string that lists each problem, like [{"fieldId":"email","reason":"invalid_email"}].

List submissions

GET /v1/forms/{formId}/submissions
Query parameterDefaultDescription
filterallall (everything except spam), unread (unread and not spam), starred or spam.
page1The page to return.
pageSize15Submissions per page, 1 to 100.
aroundA submission ID. Returns the page that holds it instead of page, when it matches the filter.
curl "https://api.nisuform.com/v1/forms/FORM_ID/submissions?filter=unread&pageSize=100" \
  -H "Authorization: Bearer $NISUFORM_API_KEY"

Submissions come newest first. Each item is a submission object with a preview, the longest answer cut to a short snippet.

{
  "items": [
    {
      "id": "8d0f5a4e-3b9c-4f7a-9e21-6c4d2b1a0f93",
      "preview": "Hello!",
      "data": { "name": "Jane Doe", "email": "jane@example.com", "message": "Hello!" },
      "read": false
    }
  ],
  "page": 1,
  "pageSize": 100,
  "pageCount": 1,
  "total": 1,
  "unreadCount": 1,
  "asOf": "2026-09-29T10:00:00.000Z"
}
FieldDescription
itemsThe submissions on this page.
pageThe page you got. Asking for a page past the end returns the last page.
pageSizeSubmissions per page.
pageCountNumber of pages, at least 1.
totalSubmissions that match the filter.
unreadCountUnread submissions in the form, whatever the filter.
asOfWhen the list was read. Pass it to update or delete by filter.

Page through every submission

const API = 'https://api.nisuform.com/v1'
const headers = { Authorization: `Bearer ${process.env.NISUFORM_API_KEY}` }

async function allSubmissions(formId) {
  const seen = new Map()
  for (let page = 1; ; page++) {
    const response = await fetch(`${API}/forms/${formId}/submissions?page=${page}&pageSize=100`, { headers })
    if (!response.ok) throw new Error(`Nisuform answered ${response.status}`)
    const body = await response.json()
    for (const item of body.items) seen.set(item.id, item)
    if (page >= body.pageCount) return [...seen.values()]
  }
}

Submissions that arrive while you page move older ones back, so the same submission can show up on two pages. Keep them by id, as above. For a complete copy in one request, use the CSV export.

Get a submission

GET /v1/forms/{formId}/submissions/{id}

Returns one submission object, or 404. Getting a submission doesn't mark it read.

Update submissions

PATCH /v1/forms/{formId}/submissions

Sets read, starred or isSpam on many submissions at once. Pass at least one of them, and a target.

Body fieldDescription
targetEither { "ids": [...] } with 1 to 1,000 submission IDs, or { "filter": "...", "asOf": "..." } to act on every submission that matches the filter and was received up to asOf.
readtrue or false.
starredtrue or false.
isSpamtrue moves them to spam, false moves them back.
curl -X PATCH https://api.nisuform.com/v1/forms/FORM_ID/submissions \
  -H "Authorization: Bearer $NISUFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"target":{"ids":["8d0f5a4e-3b9c-4f7a-9e21-6c4d2b1a0f93"]},"read":true}'

Answers { "count": 1 } with the number of submissions that changed. Submissions that already had the value aren't counted. IDs from other forms are ignored.

To mark everything read that you just fetched, pass the list's asOf, so submissions that arrived in the meantime stay unread:

{ "target": { "filter": "unread", "asOf": "2026-09-29T10:00:00.000Z" }, "read": true }

Delete submissions

DELETE /v1/forms/{formId}/submissions

Takes a JSON body with the same target as update, and deletes the submissions with their uploaded files. Answers { "count": 3 } with the number deleted. This can't be undone.

curl -X DELETE https://api.nisuform.com/v1/forms/FORM_ID/submissions \
  -H "Authorization: Bearer $NISUFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"target":{"filter":"spam","asOf":"2026-09-29T10:00:00.000Z"}}'

Deleted submissions still count toward this month's usage.

Download a file

GET /v1/forms/{formId}/submissions/{id}/files/{index}

index is the file's position in meta.files, starting at 0. Answers with a signed link:

{
  "url": "https://api.nisuform.com/attachments/...",
  "name": "brief.pdf",
  "mime": "application/pdf",
  "expiresAt": "2026-09-29T10:15:00.000Z"
}

The link works without an API key and expires after 15 minutes. Ask for a new one each time you need the file.

Export as CSV

GET /v1/forms/{formId}/export.csv
Query parameterDefaultDescription
filterallall, unread, starred or spam.
curl "https://api.nisuform.com/v1/forms/FORM_ID/export.csv" \
  -H "Authorization: Bearer $NISUFORM_API_KEY" \
  -o submissions.csv

Streams every matching submission as a CSV file, newest first, with the same columns as the dashboard export. It counts as one request toward the rate limit, however many rows it has.