Skip to main content
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.

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>.
{
  "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 after the record exists. For example, an opportunity create sets its required parent account_id alongside an enveloped stage:
{
  "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.

Patch a record

Patch records with PATCH /api/v2/public/<record_type>/{id}.
{
  "attributes": {
    "title": "CTO"
  }
}
Patch rules:
Request shapeMeaning
Field omittedLeave the field unchanged
Field present with a valueSet the field
Field present with nullClear the field, if nullable
Array field presentReplace 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.
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.
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).
{
  "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:
BranchHTTP statusmeta.action
Created new record201created
Updated existing record200updated

Common errors

Error codeUsually meansHow to recover
unknown_fieldThe 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_writableA request tried to set a read-only custom field, which exists but cannot be mutatedRemove the read-only custom field from the request
field_required_missingA required create field was omittedInclude the field or rely on a documented default
field_value_invalidA value could not be coerced into the field typeCheck the field’s value_type and write shape
record_not_foundThe record ID does not exist in the workspaceRe-query before patching or archiving
ambiguous_matchA match found more than one possible recordUse a more specific match field or resolve duplicates