Skip to main content

Metadata Upserts API

đź§° How to create, update, query, and delete Salesforce metadata via our API-backed API.

Overview​

This endpoint is a thin gateway to Salesforce’s Tooling API and Metadata API.
You send a single JSON payload and we forward it to Salesforce with your org/env credentials.

  • Use Tooling API for most CRUD on metadata records (e.g., Validation Rules).
  • Use Metadata API for operations that Tooling can’t delete, like removing Custom Fields.

Endpoint & Auth​

Send requests with these headers:

  • fl-api-org: your org key (e.g. opportunitynow)
  • fl-api-env: environment key (e.g., dev, prod)
  • fl-api-token: your Flourish API token

Request Body (JSON)​

{
"method": "POST | PATCH | PUT | DELETE | GET",
"path": "<salesforce-path-fragment>",
"sfApiVersion": "64.0",
"metadata": { /* optional JSON body forwarded to Salesforce */ }
}

Fields​

  • method: HTTP verb forwarded to Salesforce.
  • path:
    • Relative to /services/data/vXX.X/ (e.g., tooling/sobjects/ValidationRule), or
    • A full /services/... path (we’ll use it as-is), or
    • Special mdapi/... paths for Metadata API deletes (see below).
  • sfApiVersion: Defaults to 64.0. v64.0 is also accepted.
  • metadata: The JSON body to send to Salesforce (for POST/PATCH/PUT).

    We also accept "payload" for backward compatibility.


Quick Start Examples​

1) Create (Upsert) a Validation Rule (Tooling API)​

Create a Validation Rule on Account requiring Phone.

Request

{
"method": "POST",
"path": "tooling/sobjects/ValidationRule",
"sfApiVersion": "64.0",
"metadata": {
"FullName": "Account.Require_Phone",
"Metadata": {
"active": true,
"description": "Require phone on Accounts",
"errorConditionFormula": "ISBLANK(Phone)",
"errorMessage": "Phone is required.",
"errorDisplayField": "Phone"
}
}
}

Notes

  • Tooling requires the fields inside a Metadata wrapper.
  • For “upsert” behavior in your workflow:
    1. GET the Id (see query below),
    2. If found → PATCH by Id; otherwise → POST to create.

2) Update a Validation Rule (Tooling API)​

Request

{
"method": "PATCH",
"path": "tooling/sobjects/ValidationRule/<ValidationRuleId>",
"sfApiVersion": "64.0",
"metadata": {
"Metadata": {
"active": true,
"description": "Require phone on Accounts (updated)",
"errorConditionFormula": "ISBLANK(Phone)",
"errorMessage": "Phone is required."
}
}
}

3) Delete a Validation Rule (Tooling API)​

Request

{
"method": "DELETE",
"path": "tooling/sobjects/ValidationRule/<ValidationRuleId>",
"sfApiVersion": "64.0"
}

Response

  • 204 No Content from Salesforce → returns { ok: true, status: 204 }.

4) Delete a Custom Field (Metadata API)​

Tooling cannot delete CustomField. Use the special Metadata API route:

Request

{
"method": "DELETE",
"path": "mdapi/CustomField/Account.Custom_Field_1__c",
"sfApiVersion": "64.0"
}

Behavior

  • Calls Metadata API deleteMetadata('CustomField', 'Account.Custom_Field_1__c').
  • Fails if the field is referenced (layouts, flows, formulas, reports, etc.).

5) Query with Tooling SOQL (GET)​

Find a validation rule Id on Account by its rule name:

Request

{
"method": "GET",
"path": "tooling/query?q=SELECT+Id+FROM+ValidationRule+WHERE+EntityDefinition.QualifiedApiName='Account'+AND+ValidationName='Require_Phone'",
"sfApiVersion": "64.0"
}

Tip

  • FullName isn’t filterable in Tooling; use EntityDefinition.QualifiedApiName + ValidationName.

Object & Field Discovery​

List all custom object API names​

Request

{
"method": "GET",
"path": "tooling/query?q=SELECT+QualifiedApiName+FROM+EntityDefinition+WHERE+QualifiedApiName+LIKE+'%25__c'+AND+IsDeprecatedAndHidden=false+AND+IsCustomSetting=false+ORDER+BY+QualifiedApiName",
"sfApiVersion": "64.0"
}

List all sObjects then filter custom client-side​

Request

{
"method": "GET",
"path": "sobjects",
"sfApiVersion": "64.0"
}

Filter items where custom === true.


Responses​

All responses are normalized as JSON:

{
"ok": true,
"status": 200,
"endpoint": "/services/data/v64.0/tooling/sobjects/ValidationRule/...",
"salesforceResponse": { /* parsed JSON or { "raw": "<text>" } */ },
"salesforceRequestId": "..." // if available
}
  • ok is true for 2xx responses.
  • Non-JSON responses are returned under { "raw": "<text>" } for troubleshooting.

Error Handling​

Common errors you might see from Salesforce:

  • INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY
    Missing permission or attempting an unsupported operation (e.g., Tooling delete for CustomField).
  • INVALID_TYPE, INVALID_FIELD, MALFORMED_QUERY
    Check your Tooling SOQL and API version.
  • CANNOT_DELETE_REFERENCED_ENTITY
    Remove dependencies before deleting metadata (e.g., formulas, flows).

The API itself may return:

  • 400 Unauthorized (FL04) if fl-api-token doesn’t match.
  • 400 Client is incorrectly configured (FL05) if required secrets are missing.
  • 502 Failed to get Salesforce access token (FL06) on OAuth refresh failure.

Paths Cheat-Sheet​

TaskMethodPathBody
Create Validation RulePOSTtooling/sobjects/ValidationRule{ "FullName": "...", "Metadata": { ... } }
Update Validation RulePATCHtooling/sobjects/ValidationRule/<Id>{ "Metadata": { ... } }
Delete Validation RuleDELETEtooling/sobjects/ValidationRule/<Id>none
Query Validation Rule IdGETtooling/query?q=SELECT+Id+FROM+ValidationRule+WHERE+EntityDefinition.QualifiedApiName='Account'+AND+ValidationName='Require_Phone'none
Delete Custom FieldDELETEmdapi/CustomField/Account.Custom_Field_1__cnone
List Custom ObjectsGETtooling/query?q=SELECT+QualifiedApiName+FROM+EntityDefinition+WHERE+QualifiedApiName+LIKE+'%25__c'...none
List All sObjectsGETsobjectsnone

Tooling vs. Metadata API (Brief)​

  • Tooling API

    • Works with “metadata as records.”
    • Great for querying and editing many setup components (e.g., Validation Rules) using normal REST semantics.
    • Not everything is deletable via Tooling.
  • Metadata API

    • Works with “metadata as files.”
    • Required for some operations (e.g., deleting CustomField).
    • Our API exposes a simplified mdapi/<Type>/<FullName> route for deletes.

curl Examples​

Create Validation Rule

curl -X POST "$API_URL"   -H "Content-Type: application/json"   -H "fl-api-org: YOUR_ORG"   -H "fl-api-env: YOUR_ENV"   -H "fl-api-token: YOUR_TOKEN"   -d '{
"method": "POST",
"path": "tooling/sobjects/ValidationRule",
"sfApiVersion": "64.0",
"metadata": {
"FullName": "Account.Require_Phone",
"Metadata": {
"active": true,
"description": "Require phone on Accounts",
"errorConditionFormula": "ISBLANK(Phone)",
"errorMessage": "Phone is required.",
"errorDisplayField": "Phone"
}
}
}'

Delete Custom Field

curl -X POST "$API_URL"   -H "Content-Type: application/json"   -H "fl-api-org: YOUR_ORG"   -H "fl-api-env: YOUR_ENV"   -H "fl-api-token: YOUR_TOKEN"   -d '{
"method": "DELETE",
"path": "mdapi/CustomField/Account.Custom_Field_1__c",
"sfApiVersion": "64.0"
}'