# OrgOrg API v1 — instructions for AI agents

This document describes every endpoint of the OrgOrg v1 REST API in plain markdown. Fetch it from https://orgorg.com/docs/api/v1/agents.md and keep it in context while working with the API. It is generated from the same catalog as the human docs at https://orgorg.com/docs/api/v1.

## Basics

- Base URL: `https://orgorg.com`. All endpoint paths below are relative to it.
- Every request requires an `Authorization: Bearer <token>` header.
- `GET` endpoints take parameters in the URL query string. `POST` endpoints take a JSON body with a `Content-Type: application/json` header.
- All responses are JSON. A non-2xx status means the request failed; the body describes the problem.
- Endpoints are stable, but new response fields may be added without notice — ignore fields you do not recognize.

## Getting a token

Tokens are created by a signed-in user at https://orgorg.com/docs/credentials and inherit that user's permissions in their organization. If you do not have a token, ask your user to create one there. Verify a token works with:

```sh
curl "https://orgorg.com/api/v1/auth/test" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

A valid token returns `{ "ok": true, ... }`.

## Authentication

Validate API tokens. Human-readable docs: https://orgorg.com/docs/api/v1/auth

### Test authentication

`GET /api/v1/auth/test`

Validate the API access token is valid and working

Parameters: none.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/auth/test" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  ok: boolean:
  scopes: string[]:
  message: string
}
```

## Go Links

List, search, and save go links. Human-readable docs: https://orgorg.com/docs/api/v1/go-links

### List Go Links

`GET /api/v1/go/find`

List all go links or search for go links by query

Parameters:

- `q` (string, optional) — Search query

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/go/find?q=duck" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  links: {
    name: string;
    description: string;
    url: string;
  }[]
}
```

### Save Go Link

`POST /api/v1/go/save`

Update existing or create a new Go Link.

Parameters:

- `name` (string, required) — Name of the go link to update
- `url` (string, optional) — URL of the go link
- `description` (string, optional) — Description of the go link

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/go/save" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "lunch",
    "description": "saved lunch description via api"
  }'
```

Response shape:

```ts
{
  id: number;
  name: string;
  url: string;
  description: string;
}
```

## Oncall

Page oncall and inspect rotations. Human-readable docs: https://orgorg.com/docs/api/v1/oncall

### Page Oncall

`POST /api/v1/oncall/page`

Programatically paging on-call can be done with standard REST api calls. This can be especially useful if you have monitors that alert when certain thresholds are met. Here's an example of how you could automatically page on-call, using the JavaScript Fetch API.  Access tokens to page oncall must be created in Oncall settings page https://orgorg.com/oncall.

Parameters:

- `message` (string, required) — Message to send to on-call

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/oncall/page" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "A spike of 500 errors appeared in the logs, can you take a look?"
  }'
```

Response shape:

```ts
{
  status: string;
  message: string;
  totalTime: number;
}
```

### Get all active Oncalls

`GET /api/v1/oncall/list`

Get a list of all active oncall rotations, primary and secondary for each rotation, and a list of active incidents if any exist for each oncall.

Parameters: none.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/oncall/list" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  id: string;
  name: string;
  description: string | null;
  activeShift?: {
    startDate?: Date;
    endDate?: Date;
    primary: {
      email: string;
      displayName: string;
      phone?: string;
    };
    secondary?: {
      email: string;
      displayName: string;
      phone?: string;
    };
  };
  incidents?: {
    id: number;
    onPoint: {
      email: string;
      displayName: string;
      phone?: string;
    };
    message: string;
    status: string;
    events: {
      id: number;
      profile: {
        email: string;
        displayName: string;
        phone?: string;
      };
      type: Clear | Alert | NotifyOnPoint | DuplicateAlert | Acknowledge | Escalate | AutoEscalate | MissingPhone | NoEscalationPossible | NoActiveShift | Note | UpdateOnPoint;
      channel:   Web | Sms | Voice | Slack | IncomingApi | OrgOrg;
      created: Date;
      message?: string;
      props?: NoteEventProps | UpdateOnPointEventProps | Object;
    }[];
    created: Date;
    slackChannel?: string;
    slackChannelName?: string;
    slackMessageId?: string;
  }[];
  created: Date;
}
```

## Profiles

Your org directory and org chart, with cursor pagination and incremental sync. Names, titles, departments, teams, locations, start dates, and manager relationships. Human-readable docs: https://orgorg.com/docs/api/v1/profiles

### List profiles

`GET /api/v1/profiles`

Your org directory and org chart as a paginated table: names, titles, departments, teams, locations, employment start dates, and manager relationships. Returns only current, non-hidden people, and defaults to Active so a COUNT is a true headcount (pass ?status=Active,Pending to include people who have been invited but have not joined). Requires profile:read (or profile:write). Account state (type, status, invited, joined) lives on /api/v1/users; the two join on userId.

Parameters:

- `cursor` (string, optional) — Opaque cursor from a previous response's "nextCursor". Omit for the first page.
- `limit` (number, optional) — Rows per page, 1-500. Defaults to 100.
- `updatedSince` (string, optional) — ISO 8601 timestamp (or epoch milliseconds). Returns only profiles changed since this time, and populates "removed" with anyone who left the directory. Pass back the "syncedThrough" value from your previous run.
- `type` (`User` | `Agent` | `Pet` | `all`, optional) — Profile type, or a comma-separated list, or "all". One of: User, Agent, Pet. Defaults to User so a COUNT of the response is a true headcount and never includes AI agent or pet profiles.
- `status` (`Active` | `Pending`, optional) — Account status, or a comma-separated list. One of: Active, Pending. Defaults to Active — the common case is a headcount of people who are actually here, so someone invited but not yet joined is excluded. Pass "Active,Pending" for the full directory. Suspended and inactive people are never in this resource; they are on /api/v1/users.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/profiles?limit=10" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: number;              // stable key; managerId points into this same namespace
    userId: number;          // join key to GET /api/v1/users
    email: string;
    firstName: string | null;
    lastName: string | null;
    displayName: string;
    avatarUrl: string | null;
    pronouns: string | null;
    pronunciation: string | null;
    type: 'User' | 'Agent' | 'Pet';
    title: string | null;
    department: string | null;
    team: string | null;
    division: string | null;
    organization: string | null;
    costCenter: string | null;
    orgUnit: string | null;
    employeeId: string | null;
    startDate: string | null;   // EMPLOYMENT start, ISO 8601. Not account creation.
    seniority: number | null;   // tenure percentile
    location: string | null;
    city: string | null;
    state: string | null;
    country: string | null;
    timezone: string | null;
    tagline: string | null;
    status: string | null;
    phone: string | null;
    birthday: string | null;
    socialUrls: object | null;
    managerId: number | null;   // self-join: managerId = id
    managerEmail: string | null;
    updated: string | null;     // ISO 8601
  }[];
  removed: number[];   // ids that left the directory; only when updatedSince is set
  nextCursor: string | null;
  hasMore: boolean;
  syncedThrough: string;      // ISO 8601; pass back as updatedSince
}
```

### Get a profile

`GET /api/v1/profiles/get`

One profile by id or email, with its full upward manager chain and the values of its trading card and custom fields. Unlike the list, this returns deactivated and hidden people too, so it can be used to reconcile a departure. Requires profile:read.

Parameters:

- `id` (number, optional) — The profile id. Either "id" or "email" is required.
- `email` (string, optional) — The profile email. Either "id" or "email" is required.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/profiles/get?email=user%40domain.com" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  // every field from GET /api/v1/profiles, plus:
  managerChain: { id: number; email: string; displayName: string; depth: number }[];
  // The trading card and custom field values, by field name ("field_<id>").
  // An empty value or a disabled field is absent.
  fields: Record<string, string>;
}
```

### List profile fields

`GET /api/v1/profiles/fields`

Your org's profile fields: the built-in fields, the trading card fields, and the custom fields, in one list, as the profile settings page shows them. Use it to discover which fields exist before writing to them. Requires profile:read.

Parameters: none.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/profiles/fields" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    // The key in "fields" of POST /api/v1/profiles/update: a built-in name
    // such as "city", or "field_<id>" for a trading card or custom field.
    name: string;
    label: string;
    type: string;
    // For example "basics", "tradingCardFront", "tradingCardBack", "custom".
    group: string;
    groupLabel: string;
    kind: 'BuiltIn' | 'Org';
    enabled: boolean;
    userEditable: boolean;
  }[];
}
```

### Update a profile

`POST /api/v1/profiles/update`

Update profile fields for one person. Requires profile:write, which only an Administrator or Owner can grant, or profile:write:self, which any member can grant and which updates only the token owner's profile. A token whose owner is not an admin updates only its owner's profile, and only the fields with "userEditable" true.

Parameters:

- `email` (string, required) — The email of the person to update.
- `fields` (object, required) — Map of profile field name to value (see GET /api/v1/profiles/fields for valid names). A trading card or custom field is "field_<id>", and its value is a string. An empty string clears it.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/profiles/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@domain.com",
    "fields": {
      "city": "Mountain View",
      "state": "CA"
    }
  }'
```

Response shape:

```ts
{
  id: number;
  email: string;
  updated: boolean;
}
```

## User accounts

Account type, lifecycle status, and invite, join, and last-seen timestamps for your billable users. Administrator-only. Human-readable docs: https://orgorg.com/docs/api/v1/users

### List user accounts

`GET /api/v1/users`

Account state for your billable users: type, lifecycle status, and invite/join/last-seen timestamps. Requires user:read, which only an Administrator or Owner can grant, because members cannot see this in the app. Unlike /api/v1/profiles this is NOT filtered to active people, which is what makes attrition analysis possible.

Parameters:

- `cursor` (string, optional) — Opaque cursor from a previous response's "nextCursor". Omit for the first page.
- `limit` (number, optional) — Rows per page, 1-500. Defaults to 100.
- `updatedSince` (string, optional) — ISO 8601 timestamp (or epoch milliseconds). Returns only accounts changed since this time. Departures appear here as an accountStatus change, with the row retained.
- `accountStatus` (string, optional) — Comma-separated statuses to filter by. Any of: Pending, Active, Suspended, Inactive, Requested, PendingUpgrade. Defaults to all.
- `accountType` (string, optional) — Comma-separated types to filter by. Any of: Member, Administrator, Owner, ServiceAccount. Defaults to all.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/users?limit=10" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: number;         // profiles.userId joins to this
    uuid: string;       // same identifier SCIM uses for its resource id
    email: string;
    accountType: Member | Administrator | Owner | ServiceAccount;
    accountStatus: Pending | Active | Suspended | Inactive | Requested | PendingUpgrade;
    invited: string | null;   // ISO 8601
    joined: string | null;    // ACCOUNT creation, ISO 8601. Not employment start.
    lastSeen: string | null;  // ISO 8601
    updated: string | null;   // ISO 8601
  }[];
  nextCursor: string | null;
  hasMore: boolean;
  syncedThrough: string;    // ISO 8601; pass back as updatedSince
}
```

### Create or update a user account

`POST /api/v1/users/upsert`

Create a user account, or update an existing one matched by email. Requires user:write, which only an Administrator or Owner can grant. An API token cannot change an Owner: a promotion to Owner, a demotion from Owner, and a new Owner answer 403. A person does it in Settings, with a sign-in of the last 15 minutes.

Parameters:

- `email` (string, required) — The email of the user.
- `firstName` (string, optional) — The first name of the user.
- `lastName` (string, optional) — The last name of the user.
- `displayName` (string, optional) — The display name of the user.
- `accountType` (`Member` | `Administrator` | `Owner` | `ServiceAccount`, optional) — The account type of the user.
- `accountStatus` (`Pending` | `Active` | `Suspended` | `Inactive` | `Requested` | `PendingUpgrade`, optional) — The account status of the user.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/users/upsert" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@domain.com"
  }'
```

Response shape:

```ts
{
  id: number;
  email: string;
  accountType: Member | Administrator | Owner | ServiceAccount;
  accountStatus: Pending | Active | Suspended | Inactive | Requested | PendingUpgrade;
  invited: string | null;
  joined: string | null;
}
```

## Thanks

Send peer recognition. Human-readable docs: https://orgorg.com/docs/api/v1/thanks

### Give Thanks

`POST /api/v1/thanks/give`

Give thanks from one user to another. By default a token can only send thanks as its owner; a token with the 'thanks:impersonate' scope (admin-granted) may set fromEmail to any active member of the org.

Parameters:

- `fromEmail` (string, required) — The email of the user giving thanks. By default this must match the token owner's email. To attribute thanks to someone else (e.g. an automated peer-bonus flow), the token needs the 'thanks:impersonate' scope, and the sender must be an active member of the org. The 'thanks:impersonate' scope can only be granted by an Administrator or Owner when creating the token.
- `toEmail` (string, required) — The email of the user receiving thanks.
- `message` (string, required) — The message of gratitude to deliver.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/thanks/give" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fromEmail": "user1@domain.com",
    "toEmail": "user2@domain.com",
    "message": "You're amazing! Thank you so much for creating an awesome API. 😂"
  }'
```

Response shape:

```ts
{
  message: string;
  delivered: boolean;
  url: string | null; // link to the delivered thanks (null if not delivered); viewing requires a signed-in session
  from: {
    email: string;
    displayName: string;
    manager: { email: string; displayName: string } | null; // direct manager
    managerChain: { email: string; displayName: string; depth: number }[]; // upward chain (direct manager first)
  };
  to: {
    email: string;
    displayName: string;
    manager: { email: string; displayName: string } | null;
    managerChain: { email: string; displayName: string; depth: number }[];
  };
}
```

## CRM

Manage contacts, companies, deals, and customers, and read your deal and customer lifecycle stages. Human-readable docs: https://orgorg.com/docs/api/v1/crm

### List deal stages

`GET /api/v1/crm/deal-stages/list`

List your sales pipeline stages (keys, labels, and types) so integrations know which stage values exist. Requires the crm.stages:read scope.

Parameters: none.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/crm/deal-stages/list" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    key: string;
    label: string;
    type: 'Open' | 'Won' | 'Lost';
    sortOrder: number;
    colorHex: string;
    description: string;
    probability: number;
  }[]
}
```

### List contacts

`GET /api/v1/crm/contacts/list`

Paginated contacts, newest first. Requires crm.contacts:read (or :write).

Parameters:

- `page` (number, optional) — Page number, starting at 1.
- `pageSize` (number, optional) — Rows per page, up to 200. Defaults to 50.
- `search` (string, optional) — Match against name, email, and company.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/crm/contacts/list?page=1&pageSize=10" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: Contact[];
  page: number;
  pageSize: number;
  totalRows: number;
}
```

### Get contact

`GET /api/v1/crm/contacts/get`

One contact by id or email (one required; email is matched case-insensitively). Requires crm.contacts:read (or :write).

Parameters:

- `id` (string, optional) — Contact id.
- `email` (string, optional) — Contact email.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/crm/contacts/get?email=jordan%40acme.com" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    firstName: string;
    lastName: string | null;
    email: string | null;
    phone: string | null;
    title: string | null;
    companyId: string | null;
    notes: string | null;
    tags: string[];
    createdAt: number;
    updatedAt: number;
  }
}
```

### Create contact

`POST /api/v1/crm/contacts/create`

Create a contact. A strict create: an email that already belongs to a contact returns 409 with that contact, so use contacts/update or contacts/upsert to change one. companyDomain links (creating if needed) the employer. Requires crm.contacts:write.

Parameters:

- `firstName` (string, required) — First name.
- `lastName` (string, optional) — Last name.
- `email` (string, optional) — Email. Case-insensitive. Must not belong to an existing contact.
- `title` (string, optional) — Job title.
- `phone` (string, optional) — Phone number.
- `notes` (string, optional) — Free-form notes.
- `companyDomain` (string, optional) — Company domain to link, for example acme.com. Pass companyId instead when the domain matches more than one company.
- `companyId` (string, optional) — Company id to link. Wins over companyDomain.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/contacts/create" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Jordan",
    "lastName": "Lee",
    "email": "jordan@acme.com",
    "title": "Head of Ops",
    "companyDomain": "acme.com"
  }'
```

Response shape:

```ts
{
  data: Contact;
}
```

### Upsert contact

`POST /api/v1/crm/contacts/upsert`

Create or update a contact keyed by email. Idempotent: only the fields you send are written, so repeated calls enrich the contact as you learn more. companyDomain links (creating if needed) the employer. Requires crm.contacts:write.

Parameters:

- `email` (string, required) — The natural key. Case-insensitive.
- `firstName` (string, optional) — First name. Defaults from the email on create.
- `lastName` (string, optional) — Last name.
- `title` (string, optional) — Job title.
- `phone` (string, optional) — Phone number.
- `notes` (string, optional) — Free-form notes.
- `companyDomain` (string, optional) — Company domain to link, for example acme.com.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/contacts/upsert" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jordan@acme.com",
    "firstName": "Jordan",
    "title": "Head of Ops",
    "companyDomain": "acme.com"
  }'
```

Response shape:

```ts
{
  data: Contact;
  created: boolean;
}
```

### Update contact

`POST /api/v1/crm/contacts/update`

Update a contact by id. Only the fields you send change. Requires crm.contacts:write.

Parameters:

- `id` (string, required) — Contact id.
- `firstName` (string, optional) — First name.
- `lastName` (string, optional) — Last name.
- `email` (string, optional) — Email address.
- `title` (string, optional) — Job title.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/contacts/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "CONTACT_ID",
    "title": "VP of Ops"
  }'
```

Response shape:

```ts
{
  data: Contact;
  updated: true;
}
```

### Delete contact

`POST /api/v1/crm/contacts/delete`

Delete a contact by id. Requires crm.contacts:write.

Parameters:

- `id` (string, required) — Contact id.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/contacts/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "CONTACT_ID"
  }'
```

Response shape:

```ts
{
  data: { id: string };
  deleted: true;
}
```

### List companies

`GET /api/v1/crm/companies/list`

Paginated companies, newest first. Requires crm.companies:read (or :write).

Parameters:

- `page` (number, optional) — Page number, starting at 1.
- `pageSize` (number, optional) — Rows per page, up to 200. Defaults to 50.
- `search` (string, optional) — Match against the company name.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/crm/companies/list?page=1&pageSize=10" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: Company[];
  page: number;
  pageSize: number;
  totalRows: number;
}
```

### Get company

`GET /api/v1/crm/companies/get`

One company by id or domain (one required; subdomains reduce to the registrable domain). Requires crm.companies:read (or :write).

Parameters:

- `id` (string, optional) — Company id.
- `domain` (string, optional) — Company domain, for example acme.com.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/crm/companies/get?domain=acme.com" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    domain: string | null;
    industry: string | null;
    notes: string | null;
    logoUrl: string | null;
    createdAt: number;
    updatedAt: number;
  }
}
```

### Create company

`POST /api/v1/crm/companies/create`

Create a company. A strict create: a domain that already belongs to a company returns 409 with that company, so use companies/update or companies/upsert to change one. A personal-mail domain such as gmail.com is rejected. Requires crm.companies:write.

Parameters:

- `name` (string, required) — Company name.
- `domain` (string, optional) — Website domain, for example acme.com. Must not belong to an existing company.
- `industry` (string, optional) — Industry.
- `notes` (string, optional) — Free-form notes.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/companies/create" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme",
    "domain": "acme.com",
    "industry": "Software"
  }'
```

Response shape:

```ts
{
  data: Company;
}
```

### Upsert company

`POST /api/v1/crm/companies/upsert`

Create or update a company keyed by domain. Idempotent: only the fields you send are written, so repeated calls enrich the company. The name defaults to the bare domain on create. Requires crm.companies:write.

Parameters:

- `domain` (string, required) — The natural key, for example acme.com.
- `name` (string, optional) — Company name.
- `industry` (string, optional) — Industry.
- `notes` (string, optional) — Free-form notes.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/companies/upsert" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "acme.com",
    "name": "Acme",
    "industry": "Software"
  }'
```

Response shape:

```ts
{
  data: Company;
  created: boolean;
}
```

### Update company

`POST /api/v1/crm/companies/update`

Update a company by id. Only the fields you send change. Requires crm.companies:write.

Parameters:

- `id` (string, required) — Company id.
- `name` (string, optional) — Company name.
- `industry` (string, optional) — Industry.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/companies/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "COMPANY_ID",
    "industry": "Fintech"
  }'
```

Response shape:

```ts
{
  data: Company;
  updated: true;
}
```

### Delete company

`POST /api/v1/crm/companies/delete`

Delete a company by id. Requires crm.companies:write.

Parameters:

- `id` (string, required) — Company id.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/companies/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "COMPANY_ID"
  }'
```

Response shape:

```ts
{
  data: { id: string };
  deleted: true;
}
```

### List deals

`GET /api/v1/crm/deals/list`

Paginated deals, newest first. Filter by companyId or stage (key or label, case-insensitive). Requires crm.deals:read (or :write).

Parameters:

- `page` (number, optional) — Page number, starting at 1.
- `pageSize` (number, optional) — Rows per page, up to 200. Defaults to 50.
- `companyId` (string, optional) — Only deals linked to this company.
- `stage` (string, optional) — Only deals in this stage (key or label).

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/crm/deals/list?page=1&pageSize=10" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: Deal[];
  page: number;
  pageSize: number;
  totalRows: number;
}
```

### Get deal

`GET /api/v1/crm/deals/get`

One deal by id. Requires crm.deals:read (or :write).

Parameters:

- `id` (string, required) — Deal id.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/crm/deals/get?id=DEAL_ID" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    stage: string;
    stageLabel: string | null;
    amountCents: number | null;
    companyId: string | null;
    contactId: string | null;
    ownerEmail: string | null;
    expectedCloseDate: number | null;
    notes: string | null;
    externalId: string | null;
    createdAt: number;
    updatedAt: number;
  }
}
```

### Create deal

`POST /api/v1/crm/deals/create`

Create a deal. companyDomain and contactEmail resolve (creating if needed) the linked records, stage accepts a key or label case-insensitively (defaulting to your first open stage), and an externalId makes the call idempotent so event-driven callers can retry safely. Requires crm.deals:write.

Parameters:

- `name` (string, required) — Deal name.
- `stage` (string, optional) — Stage key or label, for example 'qualified'.
- `amountCents` (number, optional) — Deal value in cents.
- `companyDomain` (string, optional) — Company to link by domain, creating it if needed.
- `contactEmail` (string, optional) — Contact to link by email, creating it if needed.
- `ownerEmail` (string, optional) — Deal owner, by member email. Defaults to the token owner. Must be an active member of your org.
- `expectedCloseDate` (number, optional) — Expected close date, epoch milliseconds.
- `externalId` (string, optional) — Your idempotency key. Re-sending updates the same deal instead of duplicating it.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/deals/create" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme expansion",
    "stage": "qualified",
    "amountCents": 500000,
    "companyDomain": "acme.com",
    "contactEmail": "jordan@acme.com",
    "externalId": "crm-docs-example-1"
  }'
```

Response shape:

```ts
{
  data: Deal;
  created: boolean;
}
```

### Update deal

`POST /api/v1/crm/deals/update`

Update a deal by id, or by the externalId you created it with. Setting stage moves it through your pipeline (key or label, case-insensitive). Requires crm.deals:write.

Parameters:

- `id` (string, optional) — Deal id. Send id or externalId.
- `externalId` (string, optional) — The externalId you created the deal with. Send id or externalId.
- `stage` (string, optional) — Stage key or label to move to, for example 'Won'.
- `amountCents` (number, optional) — Deal value in cents.
- `name` (string, optional) — Deal name.
- `ownerEmail` (string, optional) — Reassign the deal owner, by member email. Must be an active member of your org.
- `reason` (string, optional) — Optional note recorded with a stage change, shown in the pipeline activity history.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/deals/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "DEAL_ID",
    "stage": "Won"
  }'
```

Response shape:

```ts
{
  data: Deal;
  updated: true;
}
```

### Record a deal signal

`POST /api/v1/crm/deals/signal`

Record something your product saw happen to a deal, such as a signup, a usage milestone, or a signed order, without moving its stage. The signal shows in the deal history and on the pipeline activity page with the token that sent it, counts as activity for auto-close, and is evidence the Pipe Manager weighs when it decides a move. Name the deal by id or by externalId. Requires crm.deals:write.

Parameters:

- `id` (string, optional) — Deal id. Send id or externalId.
- `externalId` (string, optional) — The externalId you created the deal with. Send id or externalId.
- `title` (string, required) — What happened, in a sentence of up to 200 characters, for example 'Invited 5 teammates'.
- `detail` (string, optional) — More context, up to 2,000 characters.
- `occurredAt` (number, optional) — When it happened, in milliseconds since 1970. Defaults to now.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/deals/signal" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "DEAL_ID",
    "title": "Invited 5 teammates"
  }'
```

Response shape:

```ts
{
  data: { id: string; dealId: string; title: string; occurredAt: number };
  created: true;
}
```

### Delete deal

`POST /api/v1/crm/deals/delete`

Delete a deal by id. Requires crm.deals:write.

Parameters:

- `id` (string, required) — Deal id.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/crm/deals/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "DEAL_ID"
  }'
```

Response shape:

```ts
{
  data: { id: string };
  deleted: true;
}
```

## Goals

Read goals and their key results, create and update key results, and record progress check-ins. Human-readable docs: https://orgorg.com/docs/api/v1/goals

### List goals

`GET /api/v1/goals/list`

List goals in your display order. By default returns the active set (ACTIVE and PUBLISHED); pass status to see drafts or archives. Requires goals:read (or :write).

Parameters:

- `status` (`ACTIVE` | `PUBLISHED` | `DRAFT` | `ARCHIVE`, optional) — Only goals with this status.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/goals/list" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string | null;
    description: string | null;
    status: 'ACTIVE' | 'PUBLISHED' | 'DRAFT' | 'ARCHIVE';
    statusType: 'Stoplight' | 'Percentage' | 'OKR' | 'Task';
    cadence: string | null;
    startDate: number;
    endDate: number | null;
    createdAt: number;
    updatedAt: number;
  }[]
}
```

### Create goal

`POST /api/v1/goals/create`

Create a goal, owned by the token owner. Created as a DRAFT — publish it from the app when ready. Requires goals:write.

Parameters:

- `name` (string, required) — Name of the goal.
- `description` (string, optional) — What this goal is about.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/goals/create" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q3 revenue",
    "description": "Grow revenue this quarter"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string | null;
    description: string | null;
    status: 'ACTIVE' | 'PUBLISHED' | 'DRAFT' | 'ARCHIVE';
    statusType: 'Stoplight' | 'Percentage' | 'OKR' | 'Task';
    cadence: string | null;
    startDate: number;
    endDate: number | null;
    createdAt: number;
    updatedAt: number;
  };
  created: true;
}
```

### Delete goal

`POST /api/v1/goals/delete`

Delete a goal by id. Soft-deleted: its details are preserved in history. Requires goals:write.

Parameters:

- `id` (string, required) — Goal id.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/goals/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "GOAL_ID"
  }'
```

Response shape:

```ts
{
  data: { id: string };
  deleted: true;
}
```

### List key results

`GET /api/v1/key-results/list`

All key results in one goal, in the goal's display order. Requires goals:read (or :write).

Parameters:

- `goalId` (string, required) — Goal id (from /api/v1/goals/list).

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/key-results/list?goalId=GOAL_ID" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: KeyResult[];
}
```

### Get key result

`GET /api/v1/key-results/get`

One key result by id, with its check-in history (most recent first). Requires goals:read (or :write).

Parameters:

- `id` (string, required) — Key result id.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/key-results/get?id=KEY_RESULT_ID" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    goalId: string | null;
    description: string;
    status: string | null; // meaning depends on the goal's statusType
    note: string | null;
    metricName: string | null;
    baseline: string | null;
    target: string | null;
    current: string | null;
    due: number | null;
    goalMode: 'Metric' | 'Development' | null;
    source: string | null;
    createdAt: number;
    updatedAt: number;
    checkIns: {
      id: string;
      keyResultId: string;
      goalId: string;
      checkIn: number;
      current: string | null;
      note: string | null;
      status: string | null;
      createdAt: number;
    }[];
  }
}
```

### Create key result

`POST /api/v1/key-results/create`

Create a key result in a goal, owned by the token owner. Metric fields let integrations seed a measurable key result in one call. Requires goals:write.

Parameters:

- `goalId` (string, required) — Goal to create the key result in.
- `description` (string, required) — What the key result is.
- `note` (string, optional) — Free-form note.
- `metricName` (string, optional) — Name of the metric being tracked, for example 'ARR'.
- `baseline` (string, optional) — Starting value of the metric.
- `target` (string, optional) — Target value of the metric.
- `current` (string, optional) — Current value of the metric.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/key-results/create" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "goalId": "GOAL_ID",
    "description": "Grow ARR to $2M",
    "metricName": "ARR",
    "baseline": "1.2M",
    "target": "2M"
  }'
```

Response shape:

```ts
{
  data: KeyResult;
  created: true;
}
```

### Update key result

`POST /api/v1/key-results/update`

Update a key result by id. Only the fields you send change. To record progress over time, prefer the check-in endpoint. Requires goals:write.

Parameters:

- `id` (string, required) — Key result id.
- `description` (string, optional) — What the key result is.
- `note` (string, optional) — Free-form note.
- `metricName` (string, optional) — Name of the metric being tracked.
- `baseline` (string, optional) — Starting value of the metric.
- `target` (string, optional) — Target value of the metric.
- `current` (string, optional) — Current value of the metric.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/key-results/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "KEY_RESULT_ID",
    "current": "1.4M"
  }'
```

Response shape:

```ts
{
  data: KeyResult;
  updated: true;
}
```

### Delete key result

`POST /api/v1/key-results/delete`

Delete a key result by id. Its details are preserved in the goal's history. Requires goals:write.

Parameters:

- `id` (string, required) — Key result id.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/key-results/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "KEY_RESULT_ID"
  }'
```

Response shape:

```ts
{
  data: { id: string };
  deleted: true;
}
```

### Create check-in

`POST /api/v1/key-results/check-in`

Record a check-in on a key result: a point-in-time progress update with the current metric value, a status, and/or a note. Status values follow the goal's statusType (Stoplight: '', 'red', 'yellow', 'green'; Percentage: '0'..'100'). This is the endpoint for automations that push progress from external systems. Requires goals:write.

Parameters:

- `keyResultId` (string, required) — Key result to check in on.
- `current` (string, optional) — Current value of the metric.
- `status` (string, optional) — Status value matching the tracker's statusType, for example 'green'.
- `note` (string, optional) — Free-form progress note.
- `checkIn` (number, optional) — Timestamp the check-in covers, epoch milliseconds. Defaults to now.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/key-results/check-in" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "keyResultId": "KEY_RESULT_ID",
    "current": "1.5M",
    "status": "green"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    keyResultId: string;
    goalId: string;
    checkIn: number;
    current: string | null;
    note: string | null;
    status: string | null;
    createdAt: number;
  };
  created: true;
}
```

### Update check-in

`POST /api/v1/key-results/check-in/update`

Update an existing check-in by id. Only the fields you send change. Requires goals:write.

Parameters:

- `id` (string, required) — Check-in id (from a key result's checkIns).
- `current` (string, optional) — Current value of the metric.
- `status` (string, optional) — Status value matching the goal's statusType.
- `note` (string, optional) — Free-form progress note.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/key-results/check-in/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "CHECK_IN_ID",
    "current": "1.6M"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    keyResultId: string;
    goalId: string;
    checkIn: number;
    current: string | null;
    note: string | null;
    status: string | null;
    createdAt: number;
  };
  updated: true;
}
```

### Delete check-in

`POST /api/v1/key-results/check-in/delete`

Delete a check-in by id. Its details are preserved in the goal's history. Requires goals:write.

Parameters:

- `id` (string, required) — Check-in id.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/key-results/check-in/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "CHECK_IN_ID"
  }'
```

Response shape:

```ts
{
  data: { id: string };
  deleted: true;
}
```

## Groups

The groups of people that a rule keeps current. List them, read the sync destinations of one, and sync one to Slack channels. The orgorg CLI uses these endpoints for its groups commands. Human-readable docs: https://orgorg.com/docs/api/v1/groups

### List groups

`GET /api/v1/groups`

List the groups you can see, with the rule and the member count of each. Requires groups:read (or :write).

Parameters: none.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/groups" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    rule: string;
    memberCount: number;
    isUnlisted: boolean;
    lastSuccessfulRun: number | null;
    createdAt: number;
    updatedAt: number;
  }[]
}
```

### List sync destinations of a group

`GET /api/v1/groups/destinations`

The sync destinations of one group (Slack channels, Google Groups, badges), with the last run of each. A Slack destination names its channel when the OrgOrg Slack app can see it. Requires groups:read (or :write).

Parameters:

- `group` (string, required) — The group: its id (the last segment of its URL, /groups/<id>) or its exact name.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/groups/destinations?group=GROUP_ID" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    type: 'SlackBot' | 'SlackUser' | 'GoogleGroups' | 'OrgOrgBadge';
    destinationId: string;
    channel: string | null;
    addOnly: boolean;
    lastRun: number | null;
    lastSuccessfulRun: number | null;
    lastRunSucceeded: boolean | null;
  }[]
}
```

### Sync a group to Slack channels

`POST /api/v1/groups/sync-slack`

Make each matching Slack channel a sync destination of the group, through the OrgOrg Slack app, then run the sync. The app invites each member of the group to each channel. Without addOnly it also removes everyone else from the channel, except bots. The app sees a private channel only after /invite. Run it again to take up a channel made later. You must be able to edit the group. Requires groups:write.

Parameters:

- `group` (string, required) — The group: its id (the last segment of its URL, /groups/<id>) or its exact name.
- `channels` (string, required) — Channel names, comma-separated. A `*` matches any text: `internal_*` names every channel whose name starts with internal_.
- `addOnly` (boolean, optional) — True keeps everyone who is in a channel already. Default false: the channel becomes the group.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/groups/sync-slack" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "group": "GROUP_ID",
    "channels": "internal_*"
  }'
```

Response shape:

```ts
{
  data: {
    group: { id: string; name: string };
    addOnly: boolean;
    destinations: {
      destinationId: string;
      channelId: string;
      channel: string;
      isPrivate: boolean;
      created: boolean;
    }[];
    unmatched: string[];
    members: number;
  }
}
```

## Leaderboards

Create leaderboards that a process fills with counts, with place badges that move by themselves, and voting boards with contenders, votes, and ratings. Human-readable docs: https://orgorg.com/docs/api/v1/leaderboards

### List leaderboards

`GET /api/v1/leaderboards/list`

List the org's leaderboards and voting boards by updatedAt, oldest first, with a cursor. A board that changes during a walk moves after the cursor, thus pass updatedSince to sync only the changes. Requires leaderboards:read (or :write).

Parameters:

- `limit` (number, optional) — Boards per page, 1 to 500. The default is 100.
- `cursor` (string, optional) — The nextCursor of the previous page.
- `updatedSince` (number, optional) — Only boards updated at or after this time, in milliseconds since 1970.
- `kind` (`Voting` | `System` | `Counted` | `Bracket`, optional) — Only boards of this kind.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/leaderboards/list" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    emoji: string | null;
    kind: 'Voting' | 'System' | 'Counted' | 'Bracket';
    type: 'FaceOff' | 'UpDown' | 'Rate' | 'Review' | 'Leaderboard' | 'Bracket';
    systemKind: 'ThanksReceived' | 'ThanksGiven' | 'ContactsParsed' | 'CompaniesParsed' | null;
    description: string | null;
    countLabel: string | null;
    badgeMode: 'None' | 'Top1' | 'Top3';
    notifyMode: 'Gains' | 'AllChanges' | 'Silent' | null;
    createdByProfileId: number;
    createdAt: number;
    updatedAt: number;
    lastActivityAt: number;
  }[];
  nextCursor: string | null;
}
```

### Get leaderboard

`GET /api/v1/leaderboards/get`

Get one board with its top 10 rows and its row total. On a read-only leaderboard the rows are entries; on a voting board they are contenders. Requires leaderboards:read (or :write).

Parameters:

- `id` (string, required) — Leaderboard id.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/leaderboards/get?id=LEADERBOARD_ID" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    emoji: string | null;
    kind: 'Voting' | 'System' | 'Counted' | 'Bracket';
    type: 'FaceOff' | 'UpDown' | 'Rate' | 'Review' | 'Leaderboard' | 'Bracket';
    systemKind: 'ThanksReceived' | 'ThanksGiven' | 'ContactsParsed' | 'CompaniesParsed' | null;
    description: string | null;
    countLabel: string | null;
    badgeMode: 'None' | 'Top1' | 'Top3';
    notifyMode: 'Gains' | 'AllChanges' | 'Silent' | null;
    createdByProfileId: number;
    createdAt: number;
    updatedAt: number;
    lastActivityAt: number;
    top: {
      rank: number;
      kind: 'Item' | 'Member' | 'Named';
      id: string;
      name: string;
      imageUrl?: string;
      linkUrl?: string;
      profileId?: number;
      email?: string;
      count?: number;
      score?: number;
      votes?: number;
      ratingCount?: number;
    }[];
    totalRows: number;
  };
}
```

### Create leaderboard

`POST /api/v1/leaderboards/create`

Create a voting board (kind Voting, with a type) or a counted leaderboard that a process fills (kind Counted). The token owner is the creator. Only an admin creates a counted leaderboard with badges. Requires leaderboards:write.

Parameters:

- `kind` (`Voting` | `Counted`, required) — Voting or Counted.
- `name` (string, required) — Name of the board.
- `emoji` (string, optional) — An emoji for the board.
- `type` (`FaceOff` | `UpDown` | `Rate` | `Review`, optional) — Voting boards only: how people vote. The default is FaceOff.
- `description` (string, optional) — Counted leaderboards only: a line about the board.
- `countLabel` (string, optional) — Counted leaderboards only: the noun after a count, for example "pull requests merged".
- `notifyMode` (`Gains` | `AllChanges` | `Silent`, optional) — Counted leaderboards only: who hears about a place change. The default is Gains.
- `badgeMode` (`None` | `Top1` | `Top3`, optional) — Counted leaderboards only: badges for the top places. Needs an admin. The default is None.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/create" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "Counted",
    "name": "Pull requests merged",
    "countLabel": "pull requests",
    "notifyMode": "Gains"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    emoji: string | null;
    kind: 'Voting' | 'System' | 'Counted' | 'Bracket';
    type: 'FaceOff' | 'UpDown' | 'Rate' | 'Review' | 'Leaderboard' | 'Bracket';
    systemKind: 'ThanksReceived' | 'ThanksGiven' | 'ContactsParsed' | 'CompaniesParsed' | null;
    description: string | null;
    countLabel: string | null;
    badgeMode: 'None' | 'Top1' | 'Top3';
    notifyMode: 'Gains' | 'AllChanges' | 'Silent' | null;
    createdByProfileId: number;
    createdAt: number;
    updatedAt: number;
    lastActivityAt: number;
  };
  created: true;
}
```

### Update leaderboard

`POST /api/v1/leaderboards/update`

Change a board. A voting board takes name, emoji, and type. A counted leaderboard takes name, emoji, description, countLabel, notifyMode, and badgeMode. An OrgOrg leaderboard takes name, emoji, and badgeMode. A badge mode needs an admin. An omitted field stays the same. Requires leaderboards:write.

Parameters:

- `id` (string, required) — Leaderboard id.
- `name` (string, optional) — New name.
- `emoji` (string, optional) — New emoji.
- `type` (`FaceOff` | `UpDown` | `Rate` | `Review`, optional) — Voting boards only: the new voting style. Each contender gets a new score.
- `description` (string, optional) — Counted leaderboards only. An empty string clears it.
- `countLabel` (string, optional) — Counted leaderboards only. An empty string clears it.
- `notifyMode` (`Gains` | `AllChanges` | `Silent`, optional) — Read-only leaderboards only: who hears about a place change.
- `badgeMode` (`None` | `Top1` | `Top3`, optional) — Read-only leaderboards only: badges for the top places. Needs an admin. The badges move at once.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "LEADERBOARD_ID",
    "badgeMode": "Top3"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    emoji: string | null;
    kind: 'Voting' | 'System' | 'Counted' | 'Bracket';
    type: 'FaceOff' | 'UpDown' | 'Rate' | 'Review' | 'Leaderboard' | 'Bracket';
    systemKind: 'ThanksReceived' | 'ThanksGiven' | 'ContactsParsed' | 'CompaniesParsed' | null;
    description: string | null;
    countLabel: string | null;
    badgeMode: 'None' | 'Top1' | 'Top3';
    notifyMode: 'Gains' | 'AllChanges' | 'Silent' | null;
    createdByProfileId: number;
    createdAt: number;
    updatedAt: number;
    lastActivityAt: number;
  };
}
```

### Delete leaderboard

`POST /api/v1/leaderboards/delete`

Delete a board with its entries, contenders, votes, and ratings. A voting board: its creator. A counted leaderboard: its creator or an admin, and only an admin when it awards badges; its badges close. An OrgOrg leaderboard: an admin. Requires leaderboards:write.

Parameters:

- `id` (string, required) — Leaderboard id.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "LEADERBOARD_ID"
  }'
```

Response shape:

```ts
{
  data: { id: string };
  deleted: true;
}
```

### List entries

`GET /api/v1/leaderboards/entries/list`

List the entries of a read-only leaderboard in rank order: the highest count first, then the entry that reached its count first. A member the org suspended is left out, thus a page can hold fewer entries than the limit. Requires leaderboards:read (or :write).

Parameters:

- `id` (string, required) — Leaderboard id.
- `limit` (number, optional) — Entries per page, 1 to 500. The default is 100.
- `cursor` (string, optional) — The nextCursor of the previous page.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/leaderboards/entries/list?id=LEADERBOARD_ID&limit=100" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    rank: number;
    id: string;
    kind: 'Member' | 'Named';
    key: string; // member:<profileId> for a member
    count: number;
    countChangedAt: number;
    name: string;
    imageUrl: string | null;
    linkUrl: string | null;
    profileId: number | null;
    email: string | null;
  }[];
  nextCursor: string | null;
}
```

### Upsert entries

`POST /api/v1/leaderboards/entries/upsert`

Create or update entries of a counted leaderboard by key, up to 500 per request. A key is trimmed and in lowercase. An entry not in the request keeps its row. The answer has one result per entry, and one bad entry does not stop the others. An identical request writes nothing and answers Unchanged for each entry. On a board with badges, only an admin sends entries, and the badges move 5 minutes after the order changes. Requires leaderboards:write.

Parameters:

- `id` (string, required) — Leaderboard id.
- `entries` (object, required) — An array of entries. A member: { member: { email } or { profileId }, count }. A named entry: { key, name, imageUrl?, linkUrl?, count }. A count is a whole number, 0 or more.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/entries/upsert" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "LEADERBOARD_ID",
    "entries": [
      {
        "member": {
          "email": "sam@example.com"
        },
        "count": 12
      },
      {
        "key": "team-a",
        "name": "Team A",
        "count": 9
      }
    ]
  }'
```

Response shape:

```ts
{
  data: {
    results: {
      index: number;
      key: string | null;
      status: 'Created' | 'Updated' | 'Unchanged' | 'Deleted' | 'NotFound' | 'Rejected';
      error?: string;
    }[];
    orderChanged: boolean;
  };
}
```

### Replace entries

`POST /api/v1/leaderboards/entries/replace`

Make a counted leaderboard hold exactly these entries, 1 to 1000. A row not in the request is deleted, except the row of an entry that the request sends with an error. Requires leaderboards:write.

Parameters:

- `id` (string, required) — Leaderboard id.
- `entries` (object, required) — An array of entries. A member: { member: { email } or { profileId }, count }. A named entry: { key, name, imageUrl?, linkUrl?, count }. A count is a whole number, 0 or more.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/entries/replace" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "LEADERBOARD_ID",
    "entries": [
      {
        "key": "team-a",
        "name": "Team A",
        "count": 10
      },
      {
        "key": "team-b",
        "name": "Team B",
        "count": 7
      }
    ]
  }'
```

Response shape:

```ts
{
  data: {
    results: {
      index: number;
      key: string | null;
      status: 'Created' | 'Updated' | 'Unchanged' | 'Deleted' | 'NotFound' | 'Rejected';
      error?: string;
    }[];
    orderChanged: boolean;
    deleted: number;
  };
}
```

### Delete entries

`POST /api/v1/leaderboards/entries/delete`

Delete entries of a counted leaderboard by key or by member, up to 1000. A member in any status can be removed. Requires leaderboards:write.

Parameters:

- `id` (string, required) — Leaderboard id.
- `entries` (object, required) — An array of { key } or { member: { email } or { profileId } }.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/entries/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "LEADERBOARD_ID",
    "entries": [
      {
        "key": "team-b"
      }
    ]
  }'
```

Response shape:

```ts
{
  data: {
    results: {
      index: number;
      key: string | null;
      status: 'Created' | 'Updated' | 'Unchanged' | 'Deleted' | 'NotFound' | 'Rejected';
      error?: string;
    }[];
    orderChanged: boolean;
  };
}
```

### List contenders

`GET /api/v1/leaderboards/contenders/list`

List the contenders of a voting board, best score first, then the most rated. The score is the Elo rating on a face-off board, the net vote on an up/down board, and the average stars on a star board. Requires leaderboards:read (or :write).

Parameters:

- `id` (string, required) — Leaderboard id.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/leaderboards/contenders/list?id=LEADERBOARD_ID" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: ({
    id: string;
    leaderboardId: string;
    name: string;
    description: string | null;
    imageUrl: string | null;
    linkUrl: string | null;
    mapsUrl: string | null;
    score: number; // the Elo rating, the net vote, or the average stars
    eloRating: number;
    matchupVotes: number;
    ratingSum: number;
    ratingCount: number;
    addedByProfileId: number;
    createdAt: number;
  } & { rank: number })[];
}
```

### Add contender

`POST /api/v1/leaderboards/contenders/create`

Add a contender to a voting board. A board holds at most 500 contenders. Requires leaderboards:write.

Parameters:

- `id` (string, required) — Leaderboard id.
- `name` (string, required) — Name of the contender.
- `description` (string, optional) — A short description.
- `imageUrl` (string, optional) — An image URL.
- `linkUrl` (string, optional) — A link URL.
- `mapsUrl` (string, optional) — A Google Maps URL.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/contenders/create" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "LEADERBOARD_ID",
    "name": "Boba Guys"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    leaderboardId: string;
    name: string;
    description: string | null;
    imageUrl: string | null;
    linkUrl: string | null;
    mapsUrl: string | null;
    score: number; // the Elo rating, the net vote, or the average stars
    eloRating: number;
    matchupVotes: number;
    ratingSum: number;
    ratingCount: number;
    addedByProfileId: number;
    createdAt: number;
  };
  created: true;
}
```

### Update contender

`POST /api/v1/leaderboards/contenders/update`

Change a contender. The person who added it or the board creator. An omitted field stays the same, and an empty string clears an optional field. Requires leaderboards:write.

Parameters:

- `id` (string, required) — Contender id.
- `name` (string, optional) — New name.
- `description` (string, optional) — New description.
- `imageUrl` (string, optional) — New image URL.
- `linkUrl` (string, optional) — New link URL.
- `mapsUrl` (string, optional) — New Google Maps URL.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/contenders/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "CONTENDER_ID",
    "description": "Brown sugar milk tea"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    leaderboardId: string;
    name: string;
    description: string | null;
    imageUrl: string | null;
    linkUrl: string | null;
    mapsUrl: string | null;
    score: number; // the Elo rating, the net vote, or the average stars
    eloRating: number;
    matchupVotes: number;
    ratingSum: number;
    ratingCount: number;
    addedByProfileId: number;
    createdAt: number;
  };
}
```

### Delete contender

`POST /api/v1/leaderboards/contenders/delete`

Delete a contender with its votes and ratings. The person who added it or the board creator. Requires leaderboards:write.

Parameters:

- `id` (string, required) — Contender id.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/contenders/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "CONTENDER_ID"
  }'
```

Response shape:

```ts
{
  data: { id: string };
  deleted: true;
}
```

### Cast face-off vote

`POST /api/v1/leaderboards/votes/cast`

Record a face-off vote: the winner beats the loser, and both Elo ratings move. Both contenders must be on the same face-off board. Requires leaderboards:write.

Parameters:

- `winnerId` (string, required) — The contender that wins.
- `loserId` (string, required) — The contender that loses.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/votes/cast" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "winnerId": "CONTENDER_ID",
    "loserId": "OTHER_CONTENDER_ID"
  }'
```

Response shape:

```ts
{
  data: {
    winner: {
      id: string;
      leaderboardId: string;
      name: string;
      description: string | null;
      imageUrl: string | null;
      linkUrl: string | null;
      mapsUrl: string | null;
      score: number; // the Elo rating, the net vote, or the average stars
      eloRating: number;
      matchupVotes: number;
      ratingSum: number;
      ratingCount: number;
      addedByProfileId: number;
      createdAt: number;
    };
    loser: {
      id: string;
      leaderboardId: string;
      name: string;
      description: string | null;
      imageUrl: string | null;
      linkUrl: string | null;
      mapsUrl: string | null;
      score: number; // the Elo rating, the net vote, or the average stars
      eloRating: number;
      matchupVotes: number;
      ratingSum: number;
      ratingCount: number;
      addedByProfileId: number;
      createdAt: number;
    };
  };
}
```

### Set rating

`POST /api/v1/leaderboards/ratings/set`

Set the token owner's vote on an up/down board (1 or -1) or rating on a star board (1 to 5), with a review on a Review board. A second call changes the owner's rating; it does not add one. Requires leaderboards:write.

Parameters:

- `contenderId` (string, required) — Contender id.
- `value` (number, required) — 1 or -1 on an up/down board, 1 to 5 on a star board.
- `review` (string, optional) — Review boards only: the review text.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/leaderboards/ratings/set" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "contenderId": "CONTENDER_ID",
    "value": 5
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    leaderboardId: string;
    name: string;
    description: string | null;
    imageUrl: string | null;
    linkUrl: string | null;
    mapsUrl: string | null;
    score: number; // the Elo rating, the net vote, or the average stars
    eloRating: number;
    matchupVotes: number;
    ratingSum: number;
    ratingCount: number;
    addedByProfileId: number;
    createdAt: number;
  };
}
```

## Tasks

Manage tasks and the projects that hold them: create, update, complete, and delete tasks and projects. Human-readable docs: https://orgorg.com/docs/api/v1/tasks

### List projects

`GET /api/v1/projects/list`

List the projects visible to the token owner. Archived projects are hidden by default. Requires tasks:read (or :write).

Parameters:

- `includeArchived` (boolean, optional) — Set to true to include archived projects.
- `q` (string, optional) — Only projects whose name or description contains this text.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/projects/list" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    description: string;
    ownerProfileId: number | null;
    personal: boolean;
    archived: boolean;
    createdAt: number;
    updatedAt: number;
  }[]
}
```

### Get project

`GET /api/v1/projects/get`

One project by id, with its tasks. Requires tasks:read (or :write).

Parameters:

- `id` (string, required) — Project id (from /api/v1/projects/list).

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/projects/get?id=PROJECT_ID" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    description: string;
    ownerProfileId: number | null;
    personal: boolean;
    archived: boolean;
    createdAt: number;
    updatedAt: number;
    tasks: Task[]; // same shape as /api/v1/tasks/get
  }
}
```

### Create project

`POST /api/v1/projects/create`

Create a project, owned by the token owner. Requires tasks:write.

Parameters:

- `name` (string, required) — Name of the project.
- `description` (string, optional) — What this project is about.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/projects/create" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Website refresh",
    "description": "Everything for the marketing site relaunch"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    description: string;
    ownerProfileId: number | null;
    personal: boolean;
    archived: boolean;
    createdAt: number;
    updatedAt: number;
  };
  created: true;
}
```

### Update project

`POST /api/v1/projects/update`

Update a project by id. Only the fields you send change. Set archived to true to archive, false to restore. Requires tasks:write.

Parameters:

- `id` (string, required) — Project id.
- `name` (string, optional) — New name.
- `description` (string, optional) — New description.
- `archived` (boolean, optional) — true archives the project; false restores it.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/projects/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "PROJECT_ID",
    "archived": true
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    description: string;
    ownerProfileId: number | null;
    personal: boolean;
    archived: boolean;
    createdAt: number;
    updatedAt: number;
  };
  updated: true;
}
```

### Delete project

`POST /api/v1/projects/delete`

Delete a project by id. Its tasks are kept and become projectless. Prefer archiving (update with archived: true) when the history matters. Requires tasks:write.

Parameters:

- `id` (string, required) — Project id.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/projects/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "PROJECT_ID"
  }'
```

Response shape:

```ts
{
  data: { id: string };
  deleted: true;
}
```

### List tasks

`GET /api/v1/tasks/list`

Paginated tasks, newest-updated first. Returns all statuses by default; filter with the optional parameters. Requires tasks:read (or :write).

Parameters:

- `page` (number, optional) — 1-based page number. Defaults to 1.
- `pageSize` (number, optional) — Rows per page, 1-200. Defaults to 50.
- `projectId` (string, optional) — Only tasks in this project.
- `status` (`NotStarted` | `InProgress` | `Blocked` | `Complete` | `WontFix`, optional) — Only tasks with this status.
- `ownerProfileId` (number, optional) — Only tasks owned by this profile.
- `q` (string, optional) — Only tasks whose description or note contains this text.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/tasks/list?status=InProgress" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    projectId: string | null;
    description: string;
    note: string;
    status: 'NotStarted' | 'InProgress' | 'Blocked' | 'Complete' | 'WontFix';
    complete: boolean;
    priority: 0 | 1 | 2 | 3 | null; // 0=P0 (highest)
    due: number | null;
    creatorProfileId: number | null;
    ownerProfileId: number | null;
    ccProfileIds: number[];
    createdAt: number;
    updatedAt: number;
  }[];
  page: number;
  pageSize: number;
  totalRows: number;
}
```

### Get task

`GET /api/v1/tasks/get`

One task by id. Requires tasks:read (or :write).

Parameters:

- `id` (string, required) — Task id.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/tasks/get?id=TASK_ID" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    projectId: string | null;
    description: string;
    note: string;
    status: 'NotStarted' | 'InProgress' | 'Blocked' | 'Complete' | 'WontFix';
    complete: boolean;
    priority: 0 | 1 | 2 | 3 | null; // 0=P0 (highest)
    due: number | null;
    creatorProfileId: number | null;
    ownerProfileId: number | null;
    ccProfileIds: number[];
    createdAt: number;
    updatedAt: number;
  }
}
```

### Create task

`POST /api/v1/tasks/create`

Create a task, created by the token owner. An unowned task falls to its creator. The owner and each cc'd person are notified, exactly as in the app. Requires tasks:write.

Parameters:

- `description` (string, required) — What the task is.
- `projectId` (string, optional) — Project to create the task in.
- `note` (string, optional) — Free-form note.
- `status` (`NotStarted` | `InProgress` | `Blocked` | `Complete` | `WontFix`, optional) — Initial status. Defaults to NotStarted.
- `priority` (number, optional) — 0 (P0, highest) through 3 (P3).
- `due` (number, optional) — Due date, as a Unix timestamp in milliseconds.
- `creatorProfileId` (number, optional) — Profile id of the task creator (see /api/v1/profiles). Defaults to the token owner. History and notifications name this person.
- `ownerProfileId` (number, optional) — Profile id of the task owner (see /api/v1/profiles). Defaults to the creator.
- `ccProfileIds` (object, optional) — Profile ids to copy on the task, as a number array.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/tasks/create" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Draft the launch announcement",
    "priority": 1
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    projectId: string | null;
    description: string;
    note: string;
    status: 'NotStarted' | 'InProgress' | 'Blocked' | 'Complete' | 'WontFix';
    complete: boolean;
    priority: 0 | 1 | 2 | 3 | null; // 0=P0 (highest)
    due: number | null;
    creatorProfileId: number | null;
    ownerProfileId: number | null;
    ccProfileIds: number[];
    createdAt: number;
    updatedAt: number;
  };
  created: true;
}
```

### Update task

`POST /api/v1/tasks/update`

Update a task by id. Only the fields you send change. Set status to Complete or WontFix to close a task; closing notifies the owner and each cc'd person. Requires tasks:write.

Parameters:

- `id` (string, required) — Task id.
- `description` (string, optional) — New description.
- `note` (string, optional) — New note.
- `projectId` (string, optional) — Move the task into this project.
- `status` (`NotStarted` | `InProgress` | `Blocked` | `Complete` | `WontFix`, optional) — New status.
- `priority` (number, optional) — 0 (P0, highest) through 3 (P3).
- `due` (number, optional) — New due date, as a Unix timestamp in milliseconds.
- `ownerProfileId` (number, optional) — Reassign the task to this profile.
- `ccProfileIds` (object, optional) — Full replacement cc list, as a number array. An empty array clears it.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/tasks/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "TASK_ID",
    "status": "Complete"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    projectId: string | null;
    description: string;
    note: string;
    status: 'NotStarted' | 'InProgress' | 'Blocked' | 'Complete' | 'WontFix';
    complete: boolean;
    priority: 0 | 1 | 2 | 3 | null; // 0=P0 (highest)
    due: number | null;
    creatorProfileId: number | null;
    ownerProfileId: number | null;
    ccProfileIds: number[];
    createdAt: number;
    updatedAt: number;
  };
  updated: true;
}
```

### Delete task

`POST /api/v1/tasks/delete`

Delete a task by id. This is permanent; to close a task instead, use tasks/update with status Complete or WontFix. Requires tasks:write.

Parameters:

- `id` (string, required) — Task id.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/tasks/delete" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "TASK_ID"
  }'
```

Response shape:

```ts
{
  data: { id: string };
  deleted: true;
}
```

## Documents

The forms your org sends out for signature. List templates, send one to an email address, and import each signed form with its receipt and file. Human-readable docs: https://orgorg.com/docs/api/v1/documents

### List document templates

`GET /api/v1/documents/templates/list`

The forms your org sends out for signature, newest first. Requires documents:read (or :write).

Parameters: none.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/documents/templates/list" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    fileName: string;
    sizeBytes: number;
    sha256: string;
    createdAt: number;
  }[]
}
```

### List signature requests

`GET /api/v1/documents/requests/list`

Every template sent to an email address that the owner of the token can open, newest first, with its state and, once signed, its receipt: the requests they sent, and the ones shared with them. Pass status=Signed and since=<ms> to import the forms signed after your last run. Requires documents:read (or :write).

Parameters:

- `status` (`Pending` | `Waiting` | `Signed` | `Declined` | `Expired` | `Cancelled`, optional) — Only requests in this state.
- `since` (number, optional) — Only requests updated at or after this time (milliseconds since the epoch).

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/documents/requests/list?status=Signed" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    templateId: string;
    templateName: string;
    fileName: string;
    signingUrl: string;
    recipientEmail: string;
    recipientName: string | null;
    message: string | null;
    status: 'Pending' | 'Waiting' | 'Signed' | 'Declined' | 'Expired' | 'Cancelled';
    packetId: string | null;
    signerOrder: number | null;
    signerCount: number | null;
    contactId: string | null;
    expiresAt: number | null;
    openedAt: number | null;
    remindedAt: number | null;
    decline: { reason: string | null; declinedAt: number } | null;
    signature: {
      legalName: string;
      consentText: string;
      consentVersion: number;
      documentSha256: string;
      signedAt: number;
      ipAddress: string | null;
      userAgent: string | null;
      fieldValues: { fieldId: string; kind: string; label: string; value: string; drawn: boolean }[];
    } | null;
    signedDocumentUrl: string | null;
    createdAt: number;
    updatedAt: number;
  }[]
}
```

### Get a signature request

`GET /api/v1/documents/requests/get`

One request that the owner of the token can open, with its receipt and a URL for the exact file that was signed. The URL is short-lived: download it right away. A request that is not shared with the owner reads as not found. Requires documents:read (or :write).

Parameters:

- `id` (string, required) — Request id (from /api/v1/documents/requests/list).

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/documents/requests/get?id=REQUEST_ID" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    templateId: string;
    templateName: string;
    fileName: string;
    signingUrl: string;
    documentUrl: string | null;
    recipientEmail: string;
    recipientName: string | null;
    message: string | null;
    status: 'Pending' | 'Waiting' | 'Signed' | 'Declined' | 'Expired' | 'Cancelled';
    packetId: string | null;
    signerOrder: number | null;
    signerCount: number | null;
    contactId: string | null;
    expiresAt: number | null;
    openedAt: number | null;
    remindedAt: number | null;
    decline: { reason: string | null; declinedAt: number } | null;
    signature: {
      legalName: string;
      consentText: string;
      consentVersion: number;
      documentSha256: string;
      signedAt: number;
      ipAddress: string | null;
      userAgent: string | null;
      fieldValues: { fieldId: string; kind: string; label: string; value: string; drawn: boolean }[];
    } | null;
    signedDocumentUrl: string | null;
    createdAt: number;
    updatedAt: number;
  }
}
```

### Send a template for signing

`POST /api/v1/documents/requests/send`

Email a template to an address with a link to read and sign it. The recipient needs no account. Requires documents:write.

Parameters:

- `templateId` (string, required) — Template id (from /api/v1/documents/templates/list).
- `email` (string, optional) — The address that receives the signing link. Required unless signers is given.
- `signers` (object, optional) — A JSON array of signers in signing order, each { email, name?, contactId? }, at most 6. Two or more make a packet: each signer gets the link when the one before has signed, and signs on top of their signature. The response names the first request and the packetId.
- `name` (string, optional) — The recipient name, used in the email and prefilled on the signing page.
- `message` (string, optional) — A note that appears in the email and on the signing page.
- `expiresInDays` (number, optional) — Whole days until the link stops working, 1 to 365. Defaults to 2 (48 hours). Pass null for a link that never expires.
- `contactId` (string, optional) — The CRM contact this goes to. The send, the signature, and a decline write activities on it. Without it, a contact with the same email is matched.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/documents/requests/send" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "TEMPLATE_ID",
    "email": "signer@example.com",
    "name": "Alex Example"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    signingUrl: string;
    status: 'Pending';
    packetId: string | null;
    signerCount: number | null;
  };
  created: true;
}
```

## Comms

Your email templates with {{variables}}. List, create, and update them, preview a send to a recipient list, and send one personalized email per recipient from your own mailbox. Human-readable docs: https://orgorg.com/docs/api/v1/comms

### List email templates

`GET /api/v1/comms/templates/list`

List your Comms email templates, newest first, with the {{variables}} each one uses. Requires comms:read (or :write).

Parameters: none.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/comms/templates/list" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    subject: string;
    body: string;
    variables: string[];
    createdAt: number;
    updatedAt: number;
  }[]
}
```

### Create email template

`POST /api/v1/comms/templates/create`

Save a new email template. Write {{firstName}}, {{company}}, or any {{variable}} in the subject or body; a send fills it per recipient. Requires comms:write.

Parameters:

- `name` (string, required) — A short name for the template.
- `subject` (string, required) — The subject line. It can hold {{variables}}.
- `body` (string, required) — The plain-text body. It can hold {{variables}}.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/comms/templates/create" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Split repair notice",
    "subject": "An update on your {{company}} workspace",
    "body": "Hi {{firstName}},\n\nWe merged the two {{company}} workspaces into one. Nothing is lost.\n"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    subject: string;
    body: string;
    variables: string[];
    createdAt: number;
    updatedAt: number;
  };
  created: true
}
```

### Update email template

`POST /api/v1/comms/templates/update`

Change the name, subject, or body of one of your templates. Pass only the fields that change. Requires comms:write.

Parameters:

- `templateId` (string, required) — The id of the template.
- `name` (string, optional) — The new name.
- `subject` (string, optional) — The new subject line.
- `body` (string, optional) — The new plain-text body.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/comms/templates/update" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "",
    "subject": "An update on your {{company}} workspace"
  }'
```

Response shape:

```ts
{
  data: {
    id: string;
    name: string;
    subject: string;
    body: string;
    variables: string[];
    createdAt: number;
    updatedAt: number;
  }
}
```

### Preview a send

`POST /api/v1/comms/send/preview`

Render a template for every recipient without a send: the exact subject and body each one gets, the rows a missing {{variable}} holds back, invalid lines, duplicates, and the schedule (how many go today, how many spill to later days under the daily cap). Requires comms:write.

Parameters:

- `templateId` (string, required) — The id of the template.
- `recipientsText` (string, required) — The recipient list: one per line. A first line with no @ names the columns, comma or tab separated, and each column is a {{variable}}. From a shell, pass "$(cat recipients.csv)".

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/comms/send/preview" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "",
    "recipientsText": "email, firstName, company\nna@nikhil.vc, Nikhil, nikhil.vc"
  }'
```

Response shape:

```ts
{
  data: {
    recipients: {
      email: string;
      subject: string;
      bodyText: string;
      missing: string[];   // the variables this row has no value for
    }[];
    columns: string[];     // the variables the list gives
    invalid: string[];     // lines with no valid address
    duplicates: string[];
    summary: {
      sendable: number;
      blocked: number;
      perDay: { day: string; count: number }[];
      batchCap: number;
      batchCapExceeded: boolean;
      consented: boolean;  // your mailbox is enabled for sending
      accountEmail: string;
      variables: string[]; // the variables the template references
    };
  }
}
```

### Send to a list

`POST /api/v1/comms/send`

Send a template to every recipient in the list, one personalized email each, from your own mailbox. Sends are spaced minutes apart and spill to the next day past the daily cap. A row with a missing {{variable}} is skipped, never sent blank. Preview first. Requires comms:write, and a mailbox enabled for sending.

Parameters:

- `templateId` (string, required) — The id of the template.
- `recipientsText` (string, required) — The recipient list, in the same form as the preview takes.
- `excludeEmails` (object, optional) — A JSON array of addresses in the list to leave out of this send.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/comms/send" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "",
    "recipientsText": "email, firstName, company\nna@nikhil.vc, Nikhil, nikhil.vc"
  }'
```

Response shape:

```ts
{
  data: {
    mergeBatchId: string;  // the batch, for the Outbox
    scheduled: number;
    skipped: number;       // rows a missing variable held back
  }
}
```

## Environment Variables

The environments of the Environment Variables app. Resolve the variables of an environment, names only or with values, import an env file, and run a provider sync. The orgorg CLI uses these endpoints for its env commands. Human-readable docs: https://orgorg.com/docs/api/v1/env

### Resolve an environment

`GET /api/v1/env/resolve`

The variables of one environment of the Environment Variables app, resolved along its parent chain. Without reveal, the names and their defining environment. With reveal=1, the values too: the store decrypts them, logs the read to your token, and the response is never cached. With key, only the named keys: the store decrypts only those, and the read log lists only those. An API token needs the env:read scope and Read access to the environment. A service token reads the one environment it is bound to, and needs no project and no environment. The response lists the services of the project, the consumers of its values, and each variable names the services of its key by slug. With service, only the keys of the named services: the CLI materializes what the command runs.

Parameters:

- `project` (string, optional) — The project slug, for example orgorg. Required with an API token.
- `environment` (string, optional) — The environment slug, for example development. Required with an API token.
- `reveal` (string, optional) — 1 to include the values. Anything else returns names only.
- `key` (string, optional) — Only this variable. Repeat the parameter for several variables, 100 at most.
- `service` (string, optional) — Only the keys of this service, by slug. Repeat the parameter for several services.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/env/resolve?project=orgorg&environment=development" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  project: { slug: string; name: string };
  environment: { slug: string; name: string };
  variables: {
    key: string;
    value?: string;
    definedIn: string;
    public: boolean;
    services: string[]; // slugs of the services below
  }[];
  services: { slug: string; name: string }[];
}
```

### List the environments of a project

`GET /api/v1/env/project`

What the picker of `orgorg env run` shows: each environment of the project that you may read and that is not protected, with its parent, its owner, the number of values it sets itself, and the required names that it does not resolve. Also the key names of the project, and whether your token can make an environment. Nothing is decrypted. Needs an API token with the env:read scope.

Parameters:

- `project` (string, required) — The project slug, for example orgorg.
- `require` (string, optional) — A variable name that your command needs. Repeat the parameter for several names, 100 at most. Each environment lists the names that it does not resolve.

Example request:

```sh
curl -X GET "https://orgorg.com/api/v1/env/project?project=orgorg&require=CONVEX_DEPLOY_KEY" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN"
```

Response shape:

```ts
{
  project: { slug: string; name: string };
  me: { profileId: number; name: string };
  devParent: string | null;
  canCreate: boolean;
  keyNames: string[];
  environments: {
    slug: string;
    name: string;
    parent: string | null;
    isParent: boolean;
    protected: boolean;
    owner: string | null;
    mine: boolean;
    ownValues: number;
    missing: string[];
  }[];
}
```

### Make a development environment

`POST /api/v1/env/dev-environment`

Make an environment of your own that extends development, with its first values, in one step. An environment is never empty: pass at least one entry of a key that the project has. A key that the project does not have sets nothing, and the response names it. When the step fails, nothing stays behind. Needs an API token with the env:dev scope and Read access to the development environment. This scope cannot write to an environment that exists.

Parameters:

- `project` (string, required) — The project slug, for example orgorg.
- `name` (string, required) — The name of the new environment, for example dev-alex.
- `entries` (object, required) — The first values: a list of { key, value } pairs of strings, 1 to 1000 entries.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/env/dev-environment" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "project": "orgorg",
    "name": "dev-example",
    "entries": [
      {
        "key": "EXAMPLE_KEY",
        "value": "example value"
      }
    ]
  }'
```

Response shape:

```ts
{
  environment: { slug: string };
  written: number;
  unknown: string[];
}
```

### Import an env file

`POST /api/v1/env/import`

Write the entries of an env file into the environment of a service token. A key that exists gets the new value, and a new key is added for the CLI. The store encrypts each value, and the response carries counts only. Needs a service token as the bearer, not an API token.

Parameters:

- `entries` (object, required) — A list of { key, value } pairs of strings, 1 to 1000 entries.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/env/import" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "entries": [
      {
        "key": "EXAMPLE_KEY",
        "value": "example value"
      }
    ]
  }'
```

Response shape:

```ts
{
  project: { slug: string };
  environment: { slug: string };
  created: number;
  updated: number;
  keysAdded: number;
}
```

### Sync an environment with a provider

`POST /api/v1/env/sync`

Run the sync target of one service in a service token environment. A pull imports the variables of the provider into the environment. A push sends the variables of the environment to the provider. The store does the provider work, and no value comes back. Needs a service token as the bearer, not an API token.

Parameters:

- `direction` (`pull` | `push`, required) — pull or push.
- `service` (string, optional) — The slug of the service, from the services of the resolve response. Required unless provider is given.
- `provider` (`vercel` | `convex` | `github` | `cloudflare` | `gcp`, optional) — The transport kind, for a caller from before the services: it names the service the migration made for that kind.

Example request:

```sh
curl -X POST "https://orgorg.com/api/v1/env/sync" \
  -H "Authorization: Bearer $ORGORG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "pull",
    "provider": "vercel"
  }'
```

Response shape:

```ts
// pull
{
  pulled: number;
  nameOnly: string[];
  created: number;
  updated: number;
  keysAdded: number;
}
// push
{
  pushed: number;
}
```
