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).
- Relative to
- sfApiVersion: Defaults to
64.0.v64.0is 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
Metadatawrapper. - For “upsert” behavior in your workflow:
- GET the Id (see query below),
- If found →
PATCHby Id; otherwise →POSTto 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 Contentfrom 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
FullNameisn’t filterable in Tooling; useEntityDefinition.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
}
okistruefor 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 forCustomField).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)iffl-api-tokendoesn’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​
| Task | Method | Path | Body |
|---|---|---|---|
| Create Validation Rule | POST | tooling/sobjects/ValidationRule | { "FullName": "...", "Metadata": { ... } } |
| Update Validation Rule | PATCH | tooling/sobjects/ValidationRule/<Id> | { "Metadata": { ... } } |
| Delete Validation Rule | DELETE | tooling/sobjects/ValidationRule/<Id> | none |
| Query Validation Rule Id | GET | tooling/query?q=SELECT+Id+FROM+ValidationRule+WHERE+EntityDefinition.QualifiedApiName='Account'+AND+ValidationName='Require_Phone' | none |
| Delete Custom Field | DELETE | mdapi/CustomField/Account.Custom_Field_1__c | none |
| List Custom Objects | GET | tooling/query?q=SELECT+QualifiedApiName+FROM+EntityDefinition+WHERE+QualifiedApiName+LIKE+'%25__c'... | none |
| List All sObjects | GET | sobjects | none |
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"
}'