---
title: Forms
description: Capture form submissions from the browser and read them back on the server.
category: sdk
order: 3
agent: "@coduck/sdk/forms. submit(formName, fields) is PUBLIC/browser-safe (needs NEXT_PUBLIC_CODUCK_PROJECT_ID). list({formName?,limit?}) and markRead(id) require CODUCK_API_KEY (server only). FormSubmission{id,projectId,formName,fields,submitterIp,userAgent,read,createdAt}. Submissions also appear in the cloud panel Forms tab."
---

# Forms

Capture submissions from any form (contact, waitlist, feedback) without writing a backend. Submitting is browser-safe; reading requires the API key, so it runs on the server.

```ts
import { forms } from '@coduck/sdk/forms';
```

## Submit (browser-safe)

`submit()` is public — call it from a client component. It only needs `NEXT_PUBLIC_CODUCK_PROJECT_ID` (injected at deploy).

```tsx
'use client';
import { forms } from '@coduck/sdk/forms';

async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
  e.preventDefault();
  const data = new FormData(e.currentTarget);
  await forms.submit('contact', {
    name: data.get('name'),
    email: data.get('email'),
    message: data.get('message'),
  });
}
```

You can also import the bound helper directly:

```ts
import { submit } from '@coduck/sdk/forms';
await submit('waitlist', { email });
```

The `formName` is just a label you choose — submissions are grouped by it.

## Read submissions (server only)

`list()` and `markRead()` require `CODUCK_API_KEY`, so call them from a route handler or server component.

```ts
import { forms } from '@coduck/sdk/forms';

const recent = await forms.list({ formName: 'contact', limit: 50 });
await forms.markRead(recent[0].id);
```

| Method | Scope | Returns |
|---|---|---|
| `submit(formName, fields)` | browser-safe | `void` |
| `list({ formName?, limit? })` | server | `FormSubmission[]` |
| `markRead(id)` | server | `FormSubmission` |

Submissions also show up in your project's **Forms** tab in the cloud panel.

## Type

```ts
interface FormSubmission {
  id: string;
  projectId: string;
  formName: string;
  fields: Record<string, unknown>;
  submitterIp: string | null;
  userAgent: string | null;
  read: boolean;
  createdAt: string;
}
```
