Manual commercial inputs
These writes record supplied evidence; they do not pay invoices or charge a provider. Use accessible client/project IDs, stable source identifiers and expected currency. Monetary integer fields use minor units: a 12.50 major-unit amount is not an integer value of 12.
Upsert manual tracked time
Operation: commercial.timeEntry · POST /time-entries
State hours for an existing project/engagement or an explicitly unattributed row. Person values are aliases. Existing manual natural identities update rather than duplicate; no hourly price is invented. Exact scope and current creator action are required. Writes retain the owner/admin check even when a supplemental role grants an action. Foreign/missing references return 404, invalid input 400, closed/conflicting work 409. Required Idempotency-Key safely replays identical normalized requests without repeated rows/audit/mapping charges. Throttling returns 429 with Retry-After. Shared domain schemas retain money/date/unit semantics.
Required key scope: commercial:write. Current creator action: commercial.edit. Eligible plans: free, starter, growth, scale; legacy contracts: free, entry, growth. stable contract. These badges do not override current role, evidence window, quotas or target ownership.
-
Engagement/project references belong to the same workspace and each other, with current creator dataset access.
-
Raw files are bounded bytes, never multipart; filenames use valid percent encoding without control characters.
-
Partial AI aggregate imports are unavailable and deterministic mapping never calls a paid model.
-
History distinguishes uncommitted uploads, committed counts, skips and failure.
Parameters (required flags, defaults, units and bounds are the canonical schema):
View schema or synthetic response
[
{
"example": "example-commercial.timeEntry",
"in": "header",
"name": "idempotency-key",
"required": true,
"schema": {
"pattern": "^[\\x21-\\x7e]{1,200}quot;,
"type": "string"
}
}
]
Request body schema:
View schema or synthetic response
{
"content": {
"application/json": {
"examples": {
"synthetic1": {
"value": {
"billable": true,
"engagementId": "22222222-2222-4222-8222-222222222222",
"hours": 2,
"personExternalId": "example-engineer",
"projectId": "11111111-1111-4111-8111-111111111111",
"role": "Engineer",
"workedOn": "2026-10-01"
}
}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"billable": {
"default": true,
"type": "boolean"
},
"engagementId": {
"anyOf": [
{
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)quot;,
"type": "string"
},
{
"type": "null"
}
]
},
"hours": {
"maximum": 8784,
"minimum": 0,
"type": "number"
},
"note": {
"anyOf": [
{
"maxLength": 1000,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
]
},
"personExternalId": {
"anyOf": [
{
"maxLength": 512,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
]
},
"projectId": {
"anyOf": [
{
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)quot;,
"type": "string"
},
{
"type": "null"
}
]
},
"role": {
"anyOf": [
{
"maxLength": 120,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
]
},
"workedOn": {
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))quot;,
"type": "string"
},
"workItemExternalKey": {
"anyOf": [
{
"maxLength": 120,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"workedOn",
"hours"
],
"type": "object"
}
}
},
"required": true
}
Success statuses: 200, 201. The JSON response is the saved/read representation. Synthetic examples below show shape, not customer evidence.
Synthetic response:
View schema or synthetic response
{
"created": true,
"id": "11111111-1111-4111-8111-111111111111"
}
Use IDs and versions from your own preceding responses. Replace the synthetic UUIDs, dates and names in these independent examples. Set SCOPEWORTH_API_BASE and SCOPEWORTH_API_KEY only on your server.
cURL
curl --request POST "$SCOPEWORTH_API_BASE/organizations/example-org/workspaces/example-workspace/time-entries" \
--header "Authorization: Bearer $SCOPEWORTH_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: example-operation-attempt-1' \
--data-raw '{"projectId":"11111111-1111-4111-8111-111111111111","engagementId":"22222222-2222-4222-8222-222222222222","personExternalId":"example-engineer","role":"Engineer","workedOn":"2026-10-01","hours":2,"billable":true}'JavaScript fetch
// Run on your server. API_BASE ends in /api/v1 (combined host) or /v1 (verified cell).
const response = await fetch(process.env.SCOPEWORTH_API_BASE + "/organizations/example-org/workspaces/example-workspace/time-entries", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SCOPEWORTH_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "example-operation-attempt-1"
},
body: JSON.stringify({
"projectId": "11111111-1111-4111-8111-111111111111",
"engagementId": "22222222-2222-4222-8222-222222222222",
"personExternalId": "example-engineer",
"role": "Engineer",
"workedOn": "2026-10-01",
"hours": 2,
"billable": true
}),
});
if (!response.ok) throw new Error(`ScopeWorth refused request: ${response.status}`);
const result = await response.json();Python requests
import os, requests
headers = {"Authorization":"Bearer " + os.environ["SCOPEWORTH_API_KEY"],"Content-Type":"application/json","Idempotency-Key":"example-operation-attempt-1"}
response = requests.request("POST", os.environ["SCOPEWORTH_API_BASE"] + "/organizations/example-org/workspaces/example-workspace/time-entries", headers=headers, json=__import__("json").loads("{\"projectId\":\"11111111-1111-4111-8111-111111111111\",\"engagementId\":\"22222222-2222-4222-8222-222222222222\",\"personExternalId\":\"example-engineer\",\"role\":\"Engineer\",\"workedOn\":\"2026-10-01\",\"hours\":2,\"billable\":true}"), timeout=30)
response.raise_for_status()
result = response.json()For refusals, follow error recovery. A scope or plan badge is eligibility, not proof of authorization or available evidence. Read current directory IDs and versions before correcting a request. Keep an idempotency key for one unchanged mutation attempt; never blindly retry uncertain external work.
Record actual dated hours for the correct person/project. Source evidence determines their meaning.
Upsert a manual invoice
Operation: commercial.invoice · POST /invoices
State authoritative invoice totals/lines in integer minor currency units, including signed credit notes. Natural invoice identity preserves upserts. This records evidence and never issues, sends or pays an invoice. Exact scope and current creator action are required. Writes retain the owner/admin check even when a supplemental role grants an action. Foreign/missing references return 404, invalid input 400, closed/conflicting work 409. Required Idempotency-Key safely replays identical normalized requests without repeated rows/audit/mapping charges. Throttling returns 429 with Retry-After. Shared domain schemas retain money/date/unit semantics.
Required key scope: commercial:write. Current creator action: commercial.edit. Eligible plans: free, starter, growth, scale; legacy contracts: free, entry, growth. stable contract. These badges do not override current role, evidence window, quotas or target ownership.
-
Engagement/project references belong to the same workspace and each other, with current creator dataset access.
-
Raw files are bounded bytes, never multipart; filenames use valid percent encoding without control characters.
-
Partial AI aggregate imports are unavailable and deterministic mapping never calls a paid model.
-
History distinguishes uncommitted uploads, committed counts, skips and failure.
Parameters (required flags, defaults, units and bounds are the canonical schema):
View schema or synthetic response
[
{
"example": "example-commercial.invoice",
"in": "header",
"name": "idempotency-key",
"required": true,
"schema": {
"pattern": "^[\\x21-\\x7e]{1,200}quot;,
"type": "string"
}
}
]
Request body schema:
View schema or synthetic response
{
"content": {
"application/json": {
"examples": {
"synthetic1": {
"value": {
"currency": "USD",
"engagementId": "22222222-2222-4222-8222-222222222222",
"issuedOn": "2026-10-01",
"lines": [
{
"amountCents": 25000,
"description": "Example delivery"
}
],
"number": "EXAMPLE-001",
"projectId": "11111111-1111-4111-8111-111111111111",
"totalCents": 25000
}
}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"currency": {
"pattern": "^[A-Z]{3}quot;,
"type": "string"
},
"engagementId": {
"anyOf": [
{
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)quot;,
"type": "string"
},
{
"type": "null"
}
]
},
"issuedOn": {
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))quot;,
"type": "string"
},
"lines": {
"default": [],
"items": {
"properties": {
"amountCents": {
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"type": "integer"
},
"description": {
"maxLength": 1000,
"minLength": 1,
"type": "string"
},
"quantity": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
]
},
"role": {
"anyOf": [
{
"maxLength": 120,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
]
},
"unit": {
"anyOf": [
{
"enum": [
"hour",
"day",
"fixed"
],
"type": "string"
},
{
"type": "null"
}
]
},
"unitAmountCents": {
"anyOf": [
{
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"type": "integer"
},
{
"type": "null"
}
]
},
"workItemExternalKey": {
"anyOf": [
{
"maxLength": 120,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"description",
"amountCents"
],
"type": "object"
},
"maxItems": 1000,
"type": "array"
},
"number": {
"maxLength": 120,
"minLength": 1,
"type": "string"
},
"periodEnd": {
"anyOf": [
{
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))quot;,
"type": "string"
},
{
"type": "null"
}
]
},
"periodStart": {
"anyOf": [
{
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))quot;,
"type": "string"
},
{
"type": "null"
}
]
},
"projectId": {
"anyOf": [
{
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)quot;,
"type": "string"
},
{
"type": "null"
}
]
},
"status": {
"default": "sent",
"enum": [
"draft",
"sent",
"paid",
"disputed",
"void"
],
"type": "string"
},
"totalCents": {
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"type": "integer"
}
},
"required": [
"number",
"issuedOn",
"currency",
"totalCents"
],
"type": "object"
}
}
},
"required": true
}
Success statuses: 200, 201. The JSON response is the saved/read representation. Synthetic examples below show shape, not customer evidence.
Synthetic response:
View schema or synthetic response
{
"created": true,
"id": "11111111-1111-4111-8111-111111111111"
}
Use IDs and versions from your own preceding responses. Replace the synthetic UUIDs, dates and names in these independent examples. Set SCOPEWORTH_API_BASE and SCOPEWORTH_API_KEY only on your server.
cURL
curl --request POST "$SCOPEWORTH_API_BASE/organizations/example-org/workspaces/example-workspace/invoices" \
--header "Authorization: Bearer $SCOPEWORTH_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: example-operation-attempt-1' \
--data-raw '{"projectId":"11111111-1111-4111-8111-111111111111","engagementId":"22222222-2222-4222-8222-222222222222","number":"EXAMPLE-001","issuedOn":"2026-10-01","currency":"USD","totalCents":25000,"status":"sent","lines":[{"description":"Example delivery","amountCents":25000}]}'JavaScript fetch
// Run on your server. API_BASE ends in /api/v1 (combined host) or /v1 (verified cell).
const response = await fetch(process.env.SCOPEWORTH_API_BASE + "/organizations/example-org/workspaces/example-workspace/invoices", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SCOPEWORTH_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "example-operation-attempt-1"
},
body: JSON.stringify({
"projectId": "11111111-1111-4111-8111-111111111111",
"engagementId": "22222222-2222-4222-8222-222222222222",
"number": "EXAMPLE-001",
"issuedOn": "2026-10-01",
"currency": "USD",
"totalCents": 25000,
"status": "sent",
"lines": [
{
"description": "Example delivery",
"amountCents": 25000
}
]
}),
});
if (!response.ok) throw new Error(`ScopeWorth refused request: ${response.status}`);
const result = await response.json();Python requests
import os, requests
headers = {"Authorization":"Bearer " + os.environ["SCOPEWORTH_API_KEY"],"Content-Type":"application/json","Idempotency-Key":"example-operation-attempt-1"}
response = requests.request("POST", os.environ["SCOPEWORTH_API_BASE"] + "/organizations/example-org/workspaces/example-workspace/invoices", headers=headers, json=__import__("json").loads("{\"projectId\":\"11111111-1111-4111-8111-111111111111\",\"engagementId\":\"22222222-2222-4222-8222-222222222222\",\"number\":\"EXAMPLE-001\",\"issuedOn\":\"2026-10-01\",\"currency\":\"USD\",\"totalCents\":25000,\"status\":\"sent\",\"lines\":[{\"description\":\"Example delivery\",\"amountCents\":25000}]}"), timeout=30)
response.raise_for_status()
result = response.json()For refusals, follow error recovery. A scope or plan badge is eligibility, not proof of authorization or available evidence. Read current directory IDs and versions before correcting a request. Keep an idempotency key for one unchanged mutation attempt; never blindly retry uncertain external work.
Invoice totals/line composition describe the invoice, not another measure of contracted value or delivery spend.
Upsert a cloud cost line
Operation: commercial.cloudCost · POST /cloud-cost-lines
Record an explicit billing line or monthly estimate, currency and allocation. Recurring estimates have no end date and remain estimated downstream. No vendor request or invented price. Exact scope and current creator action are required. Writes retain the owner/admin check even when a supplemental role grants an action. Foreign/missing references return 404, invalid input 400, closed/conflicting work 409. Required Idempotency-Key safely replays identical normalized requests without repeated rows/audit/mapping charges. Throttling returns 429 with Retry-After. Shared domain schemas retain money/date/unit semantics.
Required key scope: commercial:write. Current creator action: commercial.edit. Eligible plans: free, starter, growth, scale; legacy contracts: free, entry, growth. stable contract. These badges do not override current role, evidence window, quotas or target ownership.
-
Engagement/project references belong to the same workspace and each other, with current creator dataset access.
-
Raw files are bounded bytes, never multipart; filenames use valid percent encoding without control characters.
-
Partial AI aggregate imports are unavailable and deterministic mapping never calls a paid model.
-
History distinguishes uncommitted uploads, committed counts, skips and failure.
Parameters (required flags, defaults, units and bounds are the canonical schema):
View schema or synthetic response
[
{
"example": "example-commercial.cloudCost",
"in": "header",
"name": "idempotency-key",
"required": true,
"schema": {
"pattern": "^[\\x21-\\x7e]{1,200}quot;,
"type": "string"
}
}
]
Request body schema:
View schema or synthetic response
{
"content": {
"application/json": {
"examples": {
"synthetic1": {
"value": {
"allocationPct": 50,
"amountCents": 12000,
"currency": "USD",
"periodEnd": "2026-10-07",
"periodStart": "2026-10-01",
"projectId": "11111111-1111-4111-8111-111111111111",
"provider": "Example cloud",
"recurring": false,
"service": "compute"
}
}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"account": {
"anyOf": [
{
"maxLength": 200,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
]
},
"allocationPct": {
"default": 100,
"maximum": 100,
"minimum": 0,
"type": "number"
},
"amountCents": {
"maximum": 9007199254740991,
"minimum": 0,
"type": "integer"
},
"category": {
"default": "other",
"enum": [
"compute",
"database",
"storage",
"network",
"observability",
"ci_cd",
"ai_api",
"other"
],
"type": "string"
},
"currency": {
"pattern": "^[A-Z]{3}quot;,
"type": "string"
},
"environment": {
"anyOf": [
{
"maxLength": 120,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
]
},
"periodEnd": {
"anyOf": [
{
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))quot;,
"type": "string"
},
{
"type": "null"
}
]
},
"periodStart": {
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))quot;,
"type": "string"
},
"projectId": {
"anyOf": [
{
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)quot;,
"type": "string"
},
{
"type": "null"
}
]
},
"provider": {
"maxLength": 120,
"minLength": 1,
"type": "string"
},
"recurring": {
"default": false,
"type": "boolean"
},
"service": {
"anyOf": [
{
"maxLength": 200,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"provider",
"periodStart",
"amountCents",
"currency"
],
"type": "object"
}
}
},
"required": true
}
Success statuses: 200, 201. The JSON response is the saved/read representation. Synthetic examples below show shape, not customer evidence.
Synthetic response:
View schema or synthetic response
{
"created": true,
"id": "11111111-1111-4111-8111-111111111111"
}
Use IDs and versions from your own preceding responses. Replace the synthetic UUIDs, dates and names in these independent examples. Set SCOPEWORTH_API_BASE and SCOPEWORTH_API_KEY only on your server.
cURL
curl --request POST "$SCOPEWORTH_API_BASE/organizations/example-org/workspaces/example-workspace/cloud-cost-lines" \
--header "Authorization: Bearer $SCOPEWORTH_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: example-operation-attempt-1' \
--data-raw '{"projectId":"11111111-1111-4111-8111-111111111111","provider":"Example cloud","service":"compute","category":"other","periodStart":"2026-10-01","periodEnd":"2026-10-07","amountCents":12000,"currency":"USD","allocationPct":50,"recurring":false}'JavaScript fetch
// Run on your server. API_BASE ends in /api/v1 (combined host) or /v1 (verified cell).
const response = await fetch(process.env.SCOPEWORTH_API_BASE + "/organizations/example-org/workspaces/example-workspace/cloud-cost-lines", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SCOPEWORTH_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "example-operation-attempt-1"
},
body: JSON.stringify({
"projectId": "11111111-1111-4111-8111-111111111111",
"provider": "Example cloud",
"service": "compute",
"category": "other",
"periodStart": "2026-10-01",
"periodEnd": "2026-10-07",
"amountCents": 12000,
"currency": "USD",
"allocationPct": 50,
"recurring": false
}),
});
if (!response.ok) throw new Error(`ScopeWorth refused request: ${response.status}`);
const result = await response.json();Python requests
import os, requests
headers = {"Authorization":"Bearer " + os.environ["SCOPEWORTH_API_KEY"],"Content-Type":"application/json","Idempotency-Key":"example-operation-attempt-1"}
response = requests.request("POST", os.environ["SCOPEWORTH_API_BASE"] + "/organizations/example-org/workspaces/example-workspace/cloud-cost-lines", headers=headers, json=__import__("json").loads("{\"projectId\":\"11111111-1111-4111-8111-111111111111\",\"provider\":\"Example cloud\",\"service\":\"compute\",\"category\":\"other\",\"periodStart\":\"2026-10-01\",\"periodEnd\":\"2026-10-07\",\"amountCents\":12000,\"currency\":\"USD\",\"allocationPct\":50,\"recurring\":false}"), timeout=30)
response.raise_for_status()
result = response.json()For refusals, follow error recovery. A scope or plan badge is eligibility, not proof of authorization or available evidence. Read current directory IDs and versions before correcting a request. Keep an idempotency key for one unchanged mutation attempt; never blindly retry uncertain external work.
Manual cloud cost is not live vendor reconciliation. Retain its source identity and date for safe upserts.
Upsert an engagement rate
Operation: commercial.rates · POST /engagements/:engagementId/rates
Set a role/deidentified-person rate on an existing engagement with explicit currency, unit and effective interval. Existing composite identity updates instead of duplicating. Exact scope and current creator action are required. Writes retain the owner/admin check even when a supplemental role grants an action. Foreign/missing references return 404, invalid input 400, closed/conflicting work 409. Required Idempotency-Key safely replays identical normalized requests without repeated rows/audit/mapping charges. Throttling returns 429 with Retry-After. Shared domain schemas retain money/date/unit semantics.
Required key scope: commercial:write. Current creator action: commercial.edit. Eligible plans: free, starter, growth, scale; legacy contracts: free, entry, growth. stable contract. These badges do not override current role, evidence window, quotas or target ownership.
-
Engagement/project references belong to the same workspace and each other, with current creator dataset access.
-
Raw files are bounded bytes, never multipart; filenames use valid percent encoding without control characters.
-
Partial AI aggregate imports are unavailable and deterministic mapping never calls a paid model.
-
History distinguishes uncommitted uploads, committed counts, skips and failure.
Parameters (required flags, defaults, units and bounds are the canonical schema):
View schema or synthetic response
[
{
"example": "22222222-2222-4222-8222-222222222222",
"in": "path",
"name": "engagementId",
"required": true,
"schema": {
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)quot;,
"type": "string"
}
},
{
"example": "example-commercial.rates",
"in": "header",
"name": "idempotency-key",
"required": true,
"schema": {
"pattern": "^[\\x21-\\x7e]{1,200}quot;,
"type": "string"
}
}
]
Request body schema:
View schema or synthetic response
{
"content": {
"application/json": {
"examples": {
"synthetic1": {
"value": {
"amountCents": 12500,
"currency": "USD",
"effectiveFrom": "2026-10-01",
"role": "Engineer",
"unit": "hour"
}
}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"amountCents": {
"maximum": 9007199254740991,
"minimum": 0,
"type": "integer"
},
"currency": {
"pattern": "^[A-Z]{3}quot;,
"type": "string"
},
"effectiveFrom": {
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))quot;,
"type": "string"
},
"effectiveTo": {
"anyOf": [
{
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))quot;,
"type": "string"
},
{
"type": "null"
}
]
},
"personExternalId": {
"anyOf": [
{
"maxLength": 512,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
]
},
"role": {
"maxLength": 120,
"minLength": 1,
"type": "string"
},
"unit": {
"enum": [
"hour",
"day"
],
"type": "string"
}
},
"required": [
"role",
"amountCents",
"currency",
"unit",
"effectiveFrom"
],
"type": "object"
}
}
},
"required": true
}
Success statuses: 200, 201. The JSON response is the saved/read representation. Synthetic examples below show shape, not customer evidence.
Synthetic response:
View schema or synthetic response
{
"created": true,
"id": "11111111-1111-4111-8111-111111111111"
}
Use IDs and versions from your own preceding responses. Replace the synthetic UUIDs, dates and names in these independent examples. Set SCOPEWORTH_API_BASE and SCOPEWORTH_API_KEY only on your server.
cURL
curl --request POST "$SCOPEWORTH_API_BASE/organizations/example-org/workspaces/example-workspace/engagements/22222222-2222-4222-8222-222222222222/rates" \
--header "Authorization: Bearer $SCOPEWORTH_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: example-operation-attempt-1' \
--data-raw '{"role":"Engineer","amountCents":12500,"currency":"USD","unit":"hour","effectiveFrom":"2026-10-01"}'JavaScript fetch
// Run on your server. API_BASE ends in /api/v1 (combined host) or /v1 (verified cell).
const response = await fetch(process.env.SCOPEWORTH_API_BASE + "/organizations/example-org/workspaces/example-workspace/engagements/22222222-2222-4222-8222-222222222222/rates", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SCOPEWORTH_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "example-operation-attempt-1"
},
body: JSON.stringify({
"role": "Engineer",
"amountCents": 12500,
"currency": "USD",
"unit": "hour",
"effectiveFrom": "2026-10-01"
}),
});
if (!response.ok) throw new Error(`ScopeWorth refused request: ${response.status}`);
const result = await response.json();Python requests
import os, requests
headers = {"Authorization":"Bearer " + os.environ["SCOPEWORTH_API_KEY"],"Content-Type":"application/json","Idempotency-Key":"example-operation-attempt-1"}
response = requests.request("POST", os.environ["SCOPEWORTH_API_BASE"] + "/organizations/example-org/workspaces/example-workspace/engagements/22222222-2222-4222-8222-222222222222/rates", headers=headers, json=__import__("json").loads("{\"role\":\"Engineer\",\"amountCents\":12500,\"currency\":\"USD\",\"unit\":\"hour\",\"effectiveFrom\":\"2026-10-01\"}"), timeout=30)
response.raise_for_status()
result = response.json()For refusals, follow error recovery. A scope or plan badge is eligibility, not proof of authorization or available evidence. Read current directory IDs and versions before correcting a request. Keep an idempotency key for one unchanged mutation attempt; never blindly retry uncertain external work.
An assumed hourly rate is distinct from a paid supplier charge. Use supported currency and numeric units.
Update an engagement budget
Operation: commercial.budget · PATCH /engagements/:engagementId/budget
Update stated contracted value, hours or both. Null removes the explicit value; absent fields stay unchanged. No invented pricing or subscription change. Exact scope and current creator action are required. Writes retain the owner/admin check even when a supplemental role grants an action. Foreign/missing references return 404, invalid input 400, closed/conflicting work 409. Required Idempotency-Key safely replays identical normalized requests without repeated rows/audit/mapping charges. Throttling returns 429 with Retry-After. Shared domain schemas retain money/date/unit semantics.
Required key scope: commercial:write. Current creator action: commercial.edit. Eligible plans: free, starter, growth, scale; legacy contracts: free, entry, growth. stable contract. These badges do not override current role, evidence window, quotas or target ownership.
-
Engagement/project references belong to the same workspace and each other, with current creator dataset access.
-
Raw files are bounded bytes, never multipart; filenames use valid percent encoding without control characters.
-
Partial AI aggregate imports are unavailable and deterministic mapping never calls a paid model.
-
History distinguishes uncommitted uploads, committed counts, skips and failure.
Parameters (required flags, defaults, units and bounds are the canonical schema):
View schema or synthetic response
[
{
"example": "22222222-2222-4222-8222-222222222222",
"in": "path",
"name": "engagementId",
"required": true,
"schema": {
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)quot;,
"type": "string"
}
},
{
"example": "example-commercial.budget",
"in": "header",
"name": "idempotency-key",
"required": true,
"schema": {
"pattern": "^[\\x21-\\x7e]{1,200}quot;,
"type": "string"
}
}
]
Request body schema:
View schema or synthetic response
{
"content": {
"application/json": {
"examples": {
"synthetic1": {
"value": {
"contractedAmountCents": 100000,
"contractedHours": 80
}
}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"contractedAmountCents": {
"anyOf": [
{
"maximum": 9007199254740991,
"minimum": 0,
"type": "integer"
},
{
"type": "null"
}
]
},
"contractedHours": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
]
}
},
"type": "object"
}
}
},
"required": true
}
Success statuses: 200. The JSON response is the saved/read representation. Synthetic examples below show shape, not customer evidence.
Synthetic response:
View schema or synthetic response
{
"updated": true
}
Use IDs and versions from your own preceding responses. Replace the synthetic UUIDs, dates and names in these independent examples. Set SCOPEWORTH_API_BASE and SCOPEWORTH_API_KEY only on your server.
cURL
curl --request PATCH "$SCOPEWORTH_API_BASE/organizations/example-org/workspaces/example-workspace/engagements/22222222-2222-4222-8222-222222222222/budget" \
--header "Authorization: Bearer $SCOPEWORTH_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: example-operation-attempt-1' \
--data-raw '{"contractedAmountCents":100000,"contractedHours":80}'JavaScript fetch
// Run on your server. API_BASE ends in /api/v1 (combined host) or /v1 (verified cell).
const response = await fetch(process.env.SCOPEWORTH_API_BASE + "/organizations/example-org/workspaces/example-workspace/engagements/22222222-2222-4222-8222-222222222222/budget", {
method: "PATCH",
headers: {
"Authorization": `Bearer ${process.env.SCOPEWORTH_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "example-operation-attempt-1"
},
body: JSON.stringify({
"contractedAmountCents": 100000,
"contractedHours": 80
}),
});
if (!response.ok) throw new Error(`ScopeWorth refused request: ${response.status}`);
const result = await response.json();Python requests
import os, requests
headers = {"Authorization":"Bearer " + os.environ["SCOPEWORTH_API_KEY"],"Content-Type":"application/json","Idempotency-Key":"example-operation-attempt-1"}
response = requests.request("PATCH", os.environ["SCOPEWORTH_API_BASE"] + "/organizations/example-org/workspaces/example-workspace/engagements/22222222-2222-4222-8222-222222222222/budget", headers=headers, json=__import__("json").loads("{\"contractedAmountCents\":100000,\"contractedHours\":80}"), timeout=30)
response.raise_for_status()
result = response.json()For refusals, follow error recovery. A scope or plan badge is eligibility, not proof of authorization or available evidence. Read current directory IDs and versions before correcting a request. Keep an idempotency key for one unchanged mutation attempt; never blindly retry uncertain external work.
Budgets are targets, not achieved revenue/savings. Ownership is checked before writes; invalid parent pairs refuse the operation. Read resulting economics through API reference and review assumptions.