> ## Documentation Index
> Fetch the complete documentation index at: https://help.reevo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Create and Update Records

> Understand create, patch, archive, and create-or-update workflows in Public API v2.

<Warning>
  **Restricted access.** Public API v2 is currently available only to allowlisted
  organizations. Requests from workspaces that have not been enabled are rejected.
  Contact your Reevo representative to have your organization added to the allowlist.
</Warning>

# Create and Update Records

Use record endpoints when your integration owns CRM data or needs to sync data from another system into Reevo.

The create, patch, and archive contract below applies to `contact`, `account`, `opportunity`, `task`, and custom objects. Two record types are read-only and reject writes: `note` returns `405 write_not_supported` on `POST`, `PATCH`, and `DELETE`, and `meeting` returns `405` on `PATCH`/`DELETE` (a meeting create has no route and returns `404 not_found`).

## Create a record

Create records with `POST /api/v2/public/<record_type>`.

```json theme={null}
{
  "attributes": {
    "first_name": "Sarah",
    "last_name": "Chen",
    "stage": { "value_type": "select", "value": { "api_name": "working" } },
    "primary_email": "sarah.chen@acme.com"
  }
}
```

The response status is `201 Created` and returns the full record in a `data` envelope.

The fields above are illustrative, not a complete required set. Required fields are workspace-specific: an admin can mark additional standard or custom (`cus_field__*`) fields required on create, so `GET /api/v2/public/_schema/{record_type}` — not this example — is the authoritative source for what a given create needs. The stage `api_name` values shown here and elsewhere in these docs (such as `working` and `proposal`) are examples; your workspace may use different stage api\_names. Check `_schema` for your workspace's required fields and valid stage values before building a create body.

Reference fields — pointers to other records, such as an opportunity's parent `account_id` or an `owner_user_id` — are set inside `attributes`, not in an inline `relationships` block. The write envelope forbids extra top-level keys, so an inline `relationships` is rejected with `400 "Extra inputs are not permitted"`. Associations beyond direct references are created through the [relationship endpoints](/Public-API-v2/Guides/Manage-Relationships) after the record exists.

For example, an opportunity create sets its required parent `account_id` alongside an enveloped `stage`:

```json theme={null}
{
  "attributes": {
    "display_name": "Acme expansion — 2026 renewal",
    "stage": { "value_type": "select", "value": { "id": "<stage-option-uuid>" } },
    "account_id": "<account-uuid>"
  }
}
```

`account_id` accepts a bare UUID (shown here), a `{ "field", "value" }` match against a unique field on the target, or a full `record_reference` envelope — see [Record References](/Public-API-v2/Value-Types/Record-References).

## Patch a record

Patch records with `PATCH /api/v2/public/<record_type>/{id}`.

```json theme={null}
{
  "attributes": {
    "title": "CTO"
  }
}
```

Patch rules:

| Request shape              | Meaning                                                       |
| -------------------------- | ------------------------------------------------------------- |
| Field omitted              | Leave the field unchanged                                     |
| Field present with a value | Set the field                                                 |
| Field present with `null`  | Clear the field, if nullable                                  |
| Array field present        | Replace the full array where parent patch supports that field |

For surgical edits to supported array-like fields, prefer sub-resource endpoints such as contact emails, contact phone numbers, and account domains.

## Archive a record

`DELETE /api/v2/public/<record_type>/{id}` archives the record rather than hard-deleting it. A successful archive returns `204 No Content`.

```bash theme={null}
curl -X DELETE "https://api.reevo.ai/api/v2/public/account/<account_id>" \
  -H "Authorization: Bearer <your-api-key>"
```

Once archived, a record no longer appears in `GET` and `_query` results. Public API v2 has no restore endpoint, so an integration cannot unarchive a record it deleted; restore it from the Reevo app if needed. A second `DELETE` on an already-archived (or absent) record returns `404 record_not_found`, not a `2xx`: the record state is unchanged, but a retrying client must treat that `404` as an acceptable "already archived" outcome. `account` is the exception: a second `DELETE` on an already-archived account returns `204`, whereas `contact`, `opportunity`, `task`, and custom objects return `404 record_not_found`.

## Create or update with `_upsert`

Use `_upsert` when you want a create-or-update workflow based on a unique field.

```text theme={null}
POST /api/v2/public/contact/_upsert
POST /api/v2/public/account/_upsert
```

`_upsert` is available for `contact` and `account` only.

The `match` object has two keys: `field` (the match-key name) and `value`. The accepted match-key names are fixed per resource:

* `contact`: `primary_email` or `contact_emails.email`
* `account`: `domains.domain_name`

An unknown or malformed match field returns `400 invalid_matching_field`; a match that resolves to more than one record returns `409 ambiguous_match`; an omitted `match` block returns `400 request_validation_failed` (the request body fails schema validation before match-specific error handling runs).

```json theme={null}
{
  "match": {
    "field": "primary_email",
    "value": "sarah.chen@acme.com"
  },
  "attributes": {
    "first_name": "Sarah",
    "last_name": "Chen",
    "stage": { "value_type": "select", "value": { "api_name": "working" } },
    "title": "CTO"
  }
}
```

Response status depends on the branch:

| Branch                  | HTTP status | `meta.action` |
| ----------------------- | ----------- | ------------- |
| Created new record      | `201`       | `created`     |
| Updated existing record | `200`       | `updated`     |

## Common errors

| Error code               | Usually means                                                                                                                                                            | How to recover                                                                                        |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| `unknown_field`          | The field API name is not declared for this record type, or a request tried to write a standard read-only or system field (which is not exposed as a writable attribute) | Check `GET /api/v2/public/_schema/{record_type}`; remove read-only and system fields from the request |
| `field_not_writable`     | A request tried to set a read-only custom field, which exists but cannot be mutated                                                                                      | Remove the read-only custom field from the request                                                    |
| `field_required_missing` | A required create field was omitted                                                                                                                                      | Include the field or rely on a documented default                                                     |
| `field_value_invalid`    | A value could not be coerced into the field type                                                                                                                         | Check the field's `value_type` and write shape                                                        |
| `record_not_found`       | The record ID does not exist in the workspace                                                                                                                            | Re-query before patching or archiving                                                                 |
| `ambiguous_match`        | A match found more than one possible record                                                                                                                              | Use a more specific match field or resolve duplicates                                                 |

## Related guides

* [Contacts](/Public-API-v2/Objects/Contacts)
* [Accounts](/Public-API-v2/Objects/Accounts)
* [Opportunities](/Public-API-v2/Objects/Opportunities)
* [Record References](/Public-API-v2/Value-Types/Record-References)
