# Calls

URL: https://developers.uidu.org/docs/domains/calls

> Fetch job openings, grant calls, and any other application-driven opportunity. Apply via the call's embedded form.

* **Use it for:** [Public website](https://developers.uidu.org/docs/paths/public-website.md) · [Server integration](https://developers.uidu.org/docs/paths/server-integration.md)
* **Reads:** public token — works in the browser
* **Writes:** none — read-only today
* **CLI:** `uidu calls`
* **Auth:** what each token can do and where it may run — [Authentication](https://developers.uidu.org/docs/foundations/authentication.md)

A **call** is anything in uidu that accepts applications: a job opening, a grant call, a residency,
a casting call. Each one carries its own application form, so a call page is a rich-text body plus
a form to submit. For job postings and their first-class applications, see
[Jobs & applications](https://developers.uidu.org/docs/domains/jobs.md).

## Mental model

```
Call                       ← listCalls (summaries) / getCall (full)
  ├─ kind                  'job' | 'open_call' | 'opportunity'
  ├─ body, cover           rich text (Tiptap JSON) and cover image URL
  ├─ quantity              number of openings
  ├─ salaryMin / salaryMax whole currency units
  ├─ expiresAt             the application deadline
  ├─ primaryAddress        for location-bound calls
  └─ form                  the application form — same shape as getForm()
        └─→ FormResponse   createFormResponse (Forms domain)
```

* **One primitive, three kinds.** `kind` is `job`, `open_call` or `opportunity`. `listCalls`
  returns all of them; filter on `kind` yourself, or use [`listJobs`](https://developers.uidu.org/docs/domains/jobs.md) for
  `job` only.
* **The list is a summary.** `listCalls` returns `id`, `name`, `cover`, `kind`, `status`,
  `quantity`, salaries, `publishedAt`, `expiresAt`, `publicUrl`, `publicPath`, `published` and a
  short `primaryAddress` (`formattedAddress`, `city`). There is no `body` and no `form`: fetch
  one call with `getCall` for those.
* **The form is inlined.** `getCall` selects `call.form` with the same `FormFields` fragment as
  `getForm`, so `call.form` goes straight into `<DynamicForm>` or any renderer you wrote for
  [Forms](https://developers.uidu.org/docs/domains/forms.md). Each question is at `call.form.inputs.edges[].node`.
* **Salaries are whole units, not cents** — unlike every other amount in uidu (see
  [Data model](https://developers.uidu.org/docs/foundations/data-model.md)). Both are `number | null`. The call's currency
  is not part of the SDK's selection, so pass it yourself when you format
  ([`formatSalaryRange`](https://developers.uidu.org/docs/domains/jobs.md#careers-page)).
* **No deadline filter.** `listCalls` takes only `first`/`after` and sends no filter, so filter
  on `expiresAt` yourself if the page should list open calls only.
* **The calls domain is read-only.** Applying is a write in another domain: submit the call's
  form with [`createFormResponse`](https://developers.uidu.org/docs/domains/forms.md#createformresponseclient-input-), or, for a
  job, record an application with [`createApplication`](https://developers.uidu.org/docs/domains/jobs.md#job-page-with-an-apply-form).
  Either way it runs in a server action with the `apiKey` client — see
  [Authentication](https://developers.uidu.org/docs/foundations/authentication.md).

## Use cases

The examples import `uidu` from `lib/uidu.ts`, a client built with `publicToken` and `apiKey`
and imported only from server code, as in [Authentication](https://developers.uidu.org/docs/foundations/authentication.md#bearer--the-workspace).

### Listing open calls

Goal: a page that lists every call still accepting applications, with its place and deadline.

```tsx
// app/calls/page.tsx
import { listCalls } from '@uidu/client';
import { uidu } from '@/lib/uidu';

export default async function CallsPage() {
  const calls = await listCalls(uidu, { first: 50 });
  const now = new Date();
  const open = calls
    .filter((c) => c.kind !== 'job') // jobs have their own careers page
    .filter((c) => !c.expiresAt || new Date(c.expiresAt as string) > now)
    .sort((a, b) =>
      String(a.expiresAt ?? '').localeCompare(String(b.expiresAt ?? '')),
    );

  return (
    <ul>
      {open.map((call) => (
        <li key={call.id}>
          <a href={`/calls/${encodeURIComponent(call.id)}`}>{call.name}</a>
          {call.primaryAddress?.city && ` · ${call.primaryAddress.city}`}
          {call.expiresAt &&
            ` · apply by ${new Date(call.expiresAt as string).toLocaleDateString()}`}
        </li>
      ))}
    </ul>
  );
}
```

* `listCalls` returns one page as a flat array (empty if none). To read every call, pass
  `ListCallsDocument` to `paginate` — see
  [Pagination & field values](https://developers.uidu.org/docs/foundations/pagination-and-field-values.md).
* Call ids are GlobalIDs: encode them in a URL and decode them in the route, as above.

### Open-call page with its application form

Goal: one call's page — body, deadline, and the application form, submitted from the server.

```tsx
// app/calls/[id]/page.tsx
import { getCall, createFormResponse } from '@uidu/client';
import { DynamicForm, RichText, type DynamicFormValues } from '@uidu/react';
import { notFound } from 'next/navigation';
import { uidu } from '@/lib/uidu';

export default async function CallPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const call = await getCall(uidu, { id: decodeURIComponent(id) });
  if (!call) notFound();

  const form = call.form;
  const formId = form?.id;

  async function apply(values: DynamicFormValues) {
    'use server';
    if (!formId)
      return { ok: false as const, errors: [{ name: 'No application form' }] };
    const result = await createFormResponse(uidu, {
      input: {
        attributes: {
          formId,
          fieldValuesAttributes: values.fieldValues.map((fv) => ({
            fieldId: fv.fieldId,
            content: { value: fv.value }, // always wrapped
          })),
          contactAttributes: values.contact && {
            email: values.contact.email,
            contactableAttributes: {
              kind: 'person',
              firstName: values.contact.firstName,
              lastName: values.contact.lastName || undefined,
            },
          },
        },
        event: 'complete!', // submit it, don't leave a draft
      },
    });
    return result?.errors?.length
      ? { ok: false as const, errors: result.errors }
      : { ok: true as const, meta: { responseId: result?.formResponse?.id } };
  }

  return (
    <article>
      <h1>{call.name}</h1>
      {call.expiresAt && (
        <p>
          Apply by {new Date(call.expiresAt as string).toLocaleDateString()}
        </p>
      )}
      {call.body && <RichText doc={call.body} />}
      {form && <DynamicForm form={form} action={apply} submitLabel="Apply" />}
    </article>
  );
}
```

* `getCall` returns `null` for an unknown id; `call.form` can be `null` too (a call that takes no
  applications).
* `<DynamicForm>` collects the candidate's first name, last name and email
  (`includeContactFields`, on by default) plus one value per visible question.
* `createFormResponse` doesn't throw on validation errors: check `errors`. A transport failure
  throws a `UiduError` — see [Errors](https://developers.uidu.org/docs/foundations/errors.md).
* **Attachments** (CV, portfolio): each media question carries its upload target at
  `call.form.inputs.edges[].node.field` — `uploadPresignedUrl` is a presigned POST as JSON
  (`{ url, fields }`), split into `uploadUrl` and `uploadParams`; follow the
  [file upload flow](https://developers.uidu.org/docs/domains/forms.md#file-uploads).
* For a call of kind `job`, record a first-class application instead, so it shows up on the
  call's board: [Job page with an apply form](https://developers.uidu.org/docs/domains/jobs.md#job-page-with-an-apply-form).

## API at a glance

Every `@uidu/client` function in **Calls**, generated from the source — the [full reference](https://developers.uidu.org/docs/reference/client.md#calls) has the rest of the SDK.

| Function                                                                                 | Kind | Auth         | CLI          | Description                                                                                          |
| ---------------------------------------------------------------------------------------- | ---- | ------------ | ------------ | ---------------------------------------------------------------------------------------------------- |
| [`getCall`](https://github.com/uidu-org/api.js/blob/main/packages/client/src/calls.ts)   | read | public token | `calls get`  | One call (a job opening or open call) with its application `form`, same shape as `getForm`, or null. |
| [`listCalls`](https://github.com/uidu-org/api.js/blob/main/packages/client/src/calls.ts) | read | public token | `calls list` | The workspace's calls as a flat array of summaries, without their forms; page with `first`/`after`.  |

**Types:** `Call`

**CLI:**

| Entity  | list | get | create | update | delete |
| ------- | :--: | :-: | :----: | :----: | :----: |
| `calls` |   ✓  |  ✓  |    —   |    —   |    —   |
