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 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.
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:
| 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 |