> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.mercoa.com/embedded-ap-ar/home/common-concepts/custom-fields-and-metadata/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.mercoa.com/_mcp/server. Custom fields, known as in Mercoa as `Metadata`, can be used to extend the functionality of the Mercoa platform. Metadata can be used to store additional information about an invoice, such as a `propertyId` or `projectId` from your system, data about where how to process the invoice in downstream systems, etc. Metadata is stored as a dictionary of key value pairs. The keys and values can be any string value. For metadata to be exposed to the end user, you should create a [metadata schema](#metadata-schemas). This allows you to specify the type of each field, and provide a list of options for the user to choose from. # Setting and getting invoice metadata Metadata is set and retrieved using the `metadata` property on the invoice object. This property is a dictionary of key value pairs. The keys and values can be any string value. ```json { "metadata": { "propertyId": "1234", "projectId": "5678" } } ``` Invoice line items can also have metadata that is independent of the invoice metadata. This is useful if you want to store metadata about a specific line item, such as a `propertyId` or `projectId` for that line item. ```json { "lineItems": [ { "description": "Line Item 1", "metadata": { "propertyId": "1234", "projectId": "5678" } } ] } ``` Metadata can be set when [creating](/api-reference/invoice/create) an invoice, or [updated](/api-reference/invoice/update) on an existing invoice. Metadata is retrieved by [getting](/api-reference/invoice/get) the invoice. # Metadata Schemas Most times, you will want the user to be able to provide metadata when they create an invoice. For example, you may want to allow the user to provide a `propertyId` when they create an invoice. This is done by adding a custom field to the invoice creation form with a metadata schema. ## Creating a metadata schema with the admin dashboard Metadata schemas can be created in the [developer dashboard](https://mercoa.com/dashboard/developers#customizations). 1. Go to [Developer Settings --> Customizations](https://mercoa.com/dashboard/developers#customizations). ![DeveloperSettings](https://lh7-us.googleusercontent.com/0g3WFIZYDCN0KCyKEDDqlLXBlwpP_8ydbViyPVWIPs6CvXqqV3fkRkA62DvngoDWO71YSlnUtPtgkoDRey2sl5pmO3hVJanLbnKQRJzwkv2QCEJ355QSiBSpWvLe2JtMHyvwznAnKJlIlghYTyOoXQI) 2. Go to Invoice Metadata and click `Add Field` ![InvoiceMetadata](https://lh7-us.googleusercontent.com/E2bViPAATCavIh5L7ohdoY_aHNZpJl4CdQKS2Y-sZdyDXQcgNXd9WfO1Q4cp6XAidYxOxXYQ0LU_OxhpSU3rJPi8SGMCTBAjz9bAhyeH3a65rMpKtfVxlItseqAynvvzVnkODh2FUY3EjMc7XsE89dE) 3. Add the field details and click `Save` ![InvoiceDetails](https://lh7-us.googleusercontent.com/9lNWXX5slPDOGrglsIjcX_TWx8YVZpd5TBBD0-TF9NY5aIQX_Sjdv0LaJzqsZRtzyZvsH68EzUr4IQfjzhyHjlLJoAUuJf9mzD3gNnYcm73NKL0dGPeke1ABoamDkyHfUxkQ023mFe0YRvnF5Hh_-os) ## Creating a metadata schema with the API You can use the [update organization api](/api-reference/organization/update) to manage your metadata schema. For each field in the schema, you can specify the following: * **key** - The name of the field. This is used as the key in the metadata dictionary. * **displayName** - The label to display to the user when they are entering the metadata. * **type** - The type of the field. This can be `STRING`, `NUMBER`, `BOOLEAN`, `DATE`, `KEY_VALUE`. * **lineItem** - Whether the field should be displayed on the invoice line item or the invoice header. * **allowMultiple** - Whether the user should be able to enter multiple values for this field. This is only applicable for `STRING` and `KEY_VALUE` fields. * **showConditions** - A list of conditions that determine whether the field should be displayed. See the [API Reference](/api-reference/organization/update#organization-update-request-metadata_schema-show_conditions) for more details. # Setting metadata options for an entity By default, the custom metadata fields will be free form fields. This makes sense for `BOOLEAN` or `DATE` fields, but for `STRING` or `KEY_VALUE` fields, you may want to provide a list of options for the user to choose from. To do this, you can [push data](/api-reference/entity/metadata/update) to specific keys in the metadata schema for each entity. For example, if you have a `propertyId` field of type `STRING`, you can push a list of property ids to the `propertyId` key in the metadata schema. This will cause the invoice creation form to display a dropdown with the list of property ids. ## STRING Example ### Request POST [https://api.mercoa.com/entity/\{entityId}/metadata/\{key}](https://api.mercoa.com/entity/\{entityId}/metadata/\{key}) **`String`** ```curl String curl -X POST https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/projectId \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '[ "proj_123", "proj_456" ]' ``` **`String`** ```python String import requests url = "https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/projectId" payload = ["proj_123", "proj_456"] headers = { "Authorization": "Bearer ", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` **`String`** ```typescript String import { MercoaClient } from "@mercoa/javascript"; const client = new MercoaClient({ token: "YOUR_TOKEN" }); await client.entity.metadata.update("ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced", "projectId", ["proj_123", "proj_456"]); ``` **`String`** ```go String package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/projectId" payload := strings.NewReader("[\n \"proj_123\",\n \"proj_456\"\n]") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "Bearer ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` **`String`** ```ruby String require 'uri' require 'net/http' url = URI("https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/projectId") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = 'Bearer ' request["Content-Type"] = 'application/json' request.body = "[\n \"proj_123\",\n \"proj_456\"\n]" response = http.request(request) puts response.read_body ``` **`String`** ```java String import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/projectId") .header("Authorization", "Bearer ") .header("Content-Type", "application/json") .body("[\n \"proj_123\",\n \"proj_456\"\n]") .asString(); ``` **`String`** ```php String request('POST', 'https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/projectId', [ 'body' => '[ "proj_123", "proj_456" ]', 'headers' => [ 'Authorization' => 'Bearer ', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` **`String`** ```csharp String using RestSharp; var client = new RestClient("https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/projectId"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", "Bearer "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "[\n \"proj_123\",\n \"proj_456\"\n]", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` **`String`** ```swift String import Foundation let headers = [ "Authorization": "Bearer ", "Content-Type": "application/json" ] let parameters = ["proj_123", "proj_456"] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/projectId")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` This will let the user choose between `prop_123` and `prop_456` in a dropdown when they create an invoice. ## KEY\_VALUE Example In the `STRING` example, the user has to choose between two ID values. If you want to provide more information about each ID, you can use the `KEY_VALUE` type. This type allows you to provide a list of key value pairs, where the key is the value that will be stored in the metadata, and the value is the label that will be displayed to the user. ### Request POST [https://api.mercoa.com/entity/\{entityId}/metadata/\{key}](https://api.mercoa.com/entity/\{entityId}/metadata/\{key}) **`KeyValue`** ```curl KeyValue curl -X POST https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/propertyId \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '[ "{key: '\''prop_123'\'', value: '\''Beach Rental'\''}", "{key: '\''prop_456'\'', value: '\''City Rental'\''}" ]' ``` **`KeyValue`** ```python KeyValue import requests url = "https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/propertyId" payload = ["{key: 'prop_123', value: 'Beach Rental'}", "{key: 'prop_456', value: 'City Rental'}"] headers = { "Authorization": "Bearer ", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` **`KeyValue`** ```typescript KeyValue import { MercoaClient } from "@mercoa/javascript"; const client = new MercoaClient({ token: "YOUR_TOKEN" }); await client.entity.metadata.update("ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced", "propertyId", ["{key: 'prop_123', value: 'Beach Rental'}", "{key: 'prop_456', value: 'City Rental'}"]); ``` **`KeyValue`** ```go KeyValue package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/propertyId" payload := strings.NewReader("[\n \"{key: 'prop_123', value: 'Beach Rental'}\",\n \"{key: 'prop_456', value: 'City Rental'}\"\n]") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "Bearer ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` **`KeyValue`** ```ruby KeyValue require 'uri' require 'net/http' url = URI("https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/propertyId") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = 'Bearer ' request["Content-Type"] = 'application/json' request.body = "[\n \"{key: 'prop_123', value: 'Beach Rental'}\",\n \"{key: 'prop_456', value: 'City Rental'}\"\n]" response = http.request(request) puts response.read_body ``` **`KeyValue`** ```java KeyValue import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/propertyId") .header("Authorization", "Bearer ") .header("Content-Type", "application/json") .body("[\n \"{key: 'prop_123', value: 'Beach Rental'}\",\n \"{key: 'prop_456', value: 'City Rental'}\"\n]") .asString(); ``` **`KeyValue`** ```php KeyValue request('POST', 'https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/propertyId', [ 'body' => '[ "{key: \'prop_123\', value: \'Beach Rental\'}", "{key: \'prop_456\', value: \'City Rental\'}" ]', 'headers' => [ 'Authorization' => 'Bearer ', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` **`KeyValue`** ```csharp KeyValue using RestSharp; var client = new RestClient("https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/propertyId"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", "Bearer "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "[\n \"{key: 'prop_123', value: 'Beach Rental'}\",\n \"{key: 'prop_456', value: 'City Rental'}\"\n]", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` **`KeyValue`** ```swift KeyValue import Foundation let headers = [ "Authorization": "Bearer ", "Content-Type": "application/json" ] let parameters = ["{key: 'prop_123', value: 'Beach Rental'}", "{key: 'prop_456', value: 'City Rental'}"] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.mercoa.com/entity/ent_a0f6ea94-0761-4a5e-a416-3c453cb7eced/metadata/propertyId")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` This will let the user choose between `Beach House` and `Mountain House` in a dropdown when they create an invoice. When the user selects one of these options, the `prop_123` or `prop_456` value will be stored in the metadata. ## Advanced KEY\_VALUE If you want further customization on how the value is displayed on the frontend for a `KEY_VALUE` type, you can pass an object as the `value` in the following shape: ```json { "title": "string", // optional "subtitle": "string", // optional "value": "string" // required } ``` The data for a `KEY_VALUE` field is a list of quoted JSON objects. This is because the data is stored as a string in the metadata, and the JSON object is parsed when the invoice is created.