Upsert Matters
curl --request POST \
--url https://api.harvey.ai/api/v2/matters \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"matters": [
{
"client_id": "10022421",
"matter_id": "M001",
"matter_name": "Acme Acquisition",
"description": "Acquisition due diligence",
"notice": "Confirm the engagement before selecting this matter."
}
]
}
'import requests
url = "https://api.harvey.ai/api/v2/matters"
payload = { "matters": [
{
"client_id": "10022421",
"matter_id": "M001",
"matter_name": "Acme Acquisition",
"description": "Acquisition due diligence",
"notice": "Confirm the engagement before selecting this matter."
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
matters: [
{
client_id: '10022421',
matter_id: 'M001',
matter_name: 'Acme Acquisition',
description: 'Acquisition due diligence',
notice: 'Confirm the engagement before selecting this matter.'
}
]
})
};
fetch('https://api.harvey.ai/api/v2/matters', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.harvey.ai/api/v2/matters",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'matters' => [
[
'client_id' => '10022421',
'matter_id' => 'M001',
'matter_name' => 'Acme Acquisition',
'description' => 'Acquisition due diligence',
'notice' => 'Confirm the engagement before selecting this matter.'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.harvey.ai/api/v2/matters"
payload := strings.NewReader("{\n \"matters\": [\n {\n \"client_id\": \"10022421\",\n \"matter_id\": \"M001\",\n \"matter_name\": \"Acme Acquisition\",\n \"description\": \"Acquisition due diligence\",\n \"notice\": \"Confirm the engagement before selecting this matter.\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.harvey.ai/api/v2/matters")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"matters\": [\n {\n \"client_id\": \"10022421\",\n \"matter_id\": \"M001\",\n \"matter_name\": \"Acme Acquisition\",\n \"description\": \"Acquisition due diligence\",\n \"notice\": \"Confirm the engagement before selecting this matter.\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.harvey.ai/api/v2/matters")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"matters\": [\n {\n \"client_id\": \"10022421\",\n \"matter_id\": \"M001\",\n \"matter_name\": \"Acme Acquisition\",\n \"description\": \"Acquisition due diligence\",\n \"notice\": \"Confirm the engagement before selecting this matter.\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"created": [
{
"client_id": "10022421",
"matter_id": "M001",
"matter_name": "Acme Acquisition",
"description": "Acquisition due diligence",
"notice": "Confirm the engagement before selecting this matter.",
"client_name": "Acme Corp",
"allowed": true,
"harvey_id": "018d856f-0664-772c-9ed9-b2316fd881d3"
}
],
"updated": [],
"errors": []
}Upsert
Upsert Matters
Creates or updates matters under existing clients using your firm’s client and matter identifiers. Submit 1 to 10,000 matters per request. Uses the delimiter configured for the API token’s active workspace, or . when none is configured. If the workspace requires an explicit delimiter and none is configured, the whole request returns HTTP 400 with delimiter_required.
POST
/
api
/
v2
/
matters
Upsert Matters
curl --request POST \
--url https://api.harvey.ai/api/v2/matters \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"matters": [
{
"client_id": "10022421",
"matter_id": "M001",
"matter_name": "Acme Acquisition",
"description": "Acquisition due diligence",
"notice": "Confirm the engagement before selecting this matter."
}
]
}
'import requests
url = "https://api.harvey.ai/api/v2/matters"
payload = { "matters": [
{
"client_id": "10022421",
"matter_id": "M001",
"matter_name": "Acme Acquisition",
"description": "Acquisition due diligence",
"notice": "Confirm the engagement before selecting this matter."
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
matters: [
{
client_id: '10022421',
matter_id: 'M001',
matter_name: 'Acme Acquisition',
description: 'Acquisition due diligence',
notice: 'Confirm the engagement before selecting this matter.'
}
]
})
};
fetch('https://api.harvey.ai/api/v2/matters', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.harvey.ai/api/v2/matters",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'matters' => [
[
'client_id' => '10022421',
'matter_id' => 'M001',
'matter_name' => 'Acme Acquisition',
'description' => 'Acquisition due diligence',
'notice' => 'Confirm the engagement before selecting this matter.'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.harvey.ai/api/v2/matters"
payload := strings.NewReader("{\n \"matters\": [\n {\n \"client_id\": \"10022421\",\n \"matter_id\": \"M001\",\n \"matter_name\": \"Acme Acquisition\",\n \"description\": \"Acquisition due diligence\",\n \"notice\": \"Confirm the engagement before selecting this matter.\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.harvey.ai/api/v2/matters")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"matters\": [\n {\n \"client_id\": \"10022421\",\n \"matter_id\": \"M001\",\n \"matter_name\": \"Acme Acquisition\",\n \"description\": \"Acquisition due diligence\",\n \"notice\": \"Confirm the engagement before selecting this matter.\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.harvey.ai/api/v2/matters")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"matters\": [\n {\n \"client_id\": \"10022421\",\n \"matter_id\": \"M001\",\n \"matter_name\": \"Acme Acquisition\",\n \"description\": \"Acquisition due diligence\",\n \"notice\": \"Confirm the engagement before selecting this matter.\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"created": [
{
"client_id": "10022421",
"matter_id": "M001",
"matter_name": "Acme Acquisition",
"description": "Acquisition due diligence",
"notice": "Confirm the engagement before selecting this matter.",
"client_name": "Acme Corp",
"allowed": true,
"harvey_id": "018d856f-0664-772c-9ed9-b2316fd881d3"
}
],
"updated": [],
"errors": []
}Permissions
Requires Client matters admin (write:client_matters). Sending allowed also requires use:disallowed_client_matters, even when the value is true; otherwise the whole request returns HTTP 403.
Before you start
Create the parent clients first. This endpoint never creates clients. If the workspace has matters created with full names, set its delimiter to match those names before using this endpoint. For an existing name such as10022421-M001, configure - and send client_id: "10022421" with matter_id: "M001". The API uses the delimiter configured for the token’s active workspace. If it is unset or empty, it uses . unless the workspace requires an explicit delimiter. In that case, the whole request returns HTTP 400 with delimiter_required; configure a delimiter before retrying. Neither path changes workspace settings.
Matching and results
The stored name isclient_id + the effective delimiter + matter_id. For client 10022421 and matter M001, the default produces 10022421.M001; a configured - produces 10022421-M001. Matching uses the identifier pair first, then the stored name. Matching soft-deleted matters are restored. Restoring a client does not restore its matters.
New matters appear in created. Matched matters appear in updated, including when their fields do not change. HTTP 200 can include row failures in errors; valid rows still succeed. Duplicate pairs and name collisions are row errors. Each successful row returns the full result. Use smaller batches to limit response size, especially with long descriptions or notices.
See the Client Matters guide for examples, field update rules, and migration steps.Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
application/json
Required array length:
1 - 10000 elementsShow child attributes
Show child attributes