Reference
Update an API key
PATCH /v1/api-keys/{id}. Change a key's label, scopes, allowlist, expiry or, for an agent key, its tools, connections, budget and cap.
PATCH /v1/api-keys/{id}
Change a key's label, scopes, allowlist, expiry or, for an agent key, its tools, connections, budget and cap. Only a signed-in person, through OAuth, may: no key changes its own limits, and an agent key never takes admin.
| Property | Value |
|---|---|
| Endpoint | PATCH https://mydatahub.emailmovers.com/v1/api-keys/{id} |
| Operation id | updateApiKey |
| Cost | Free (0 credits) |
| Access | A signed-in person only: an OAuth 2.1 token with the scope admin. API keys are refused. |
| Tag | Keys |
Request
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Format: an API key id: key_ followed by a 26 character ULID in upper case Crockford base32, such as key_01JB3Z4Q2V7XN3M9R6T1Y5W8ZA. |
MyDataHub-Version | header | string | no | The dated API version, pinned per key when left out. Non-breaking changes ship as new dates; the path major changes only for breaking changes, with a 12 month deprecation window. |
Body
| Member | Type | Required | Description |
|---|---|---|---|
label | string | no | |
scopes | array of enum: account:read, counts:read, previews:read, audiences:read, audiences:write, quotes:read, orders:read, orders:write, and 8 more | no | Replacement scopes. An agent key never takes admin, and no key takes account:write, which only a signed-in owner uses. |
ip_allowlist | array of string | no | CIDR ranges the key may be used from; empty allows any address. |
expires_at | string (date-time) or null | no | |
allowed_tools | array of enum: get_plans, get_account, list_filters, search_filter_values, expand_job_titles, resolve_location, compile_audience, count_audience, and 14 more | no | |
allowed_connection_ids | array of string | no | |
monthly_budget_credits | integer (0 or more) | no | |
per_order_max_credits | integer (0 or more) | no | |
webhook_url | string (uri) or null | no | |
disclosure | enum: account, anonymous | no | |
licence_accepted_version | string | no | The Data Licence and Sharing Agreement version the person changing the key accepts: required when the change lets an agent key spend on its own (create_order or enrich_contact, a budget and a cap above 0), or widens what it spends (a spending tool added, a larger budget or cap), and the key has not accepted the version in force. Sent alone, it renews the key's acceptance. |
Example request
curl -X PATCH "https://mydatahub.emailmovers.com/v1/api-keys/key_01JB3Z9R6T1Y5W8ZAK8Q2V7XN3" \
-H "Authorization: Bearer $MYDATAHUB_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"monthly_budget_credits": 2000
}'Responses
200: The updated key.
Headers: RateLimit, RateLimit-Policy.
StandardApiKey
| Member | Type | Always present | Description |
|---|---|---|---|
id | string | yes | Format: an API key id: key_ followed by a 26 character ULID in upper case Crockford base32, such as key_01JB3Z4Q2V7XN3M9R6T1Y5W8ZA. |
label | string | yes | |
mode | enum: live, test | yes | live keys (mdh_live_) spend real credits; test keys (mdh_test_) return synthetic data and never charge. |
prefix | string | yes | The first characters of the secret, such as mdh_live_7Kq2. |
scopes | array of enum: account:read, counts:read, previews:read, audiences:read, audiences:write, quotes:read, orders:read, orders:write, and 8 more | yes | |
ip_allowlist | array of string | yes | CIDR ranges the key may be used from; empty allows any address. |
created_at | string (date-time) | yes | |
created_by | string | yes | Format: a user id: usr_ followed by a 26 character ULID in upper case Crockford base32, such as usr_01JA0Q3V7XN3M9R6T1Y5W8ZAK8. |
last_used_at | string (date-time) or null | yes | |
expires_at | string (date-time) or null | yes | |
revoked_at | string (date-time) or null | yes | |
kind | standard | yes |
AgentKey
| Member | Type | Always present | Description |
|---|---|---|---|
id | string | yes | Format: an API key id: key_ followed by a 26 character ULID in upper case Crockford base32, such as key_01JB3Z4Q2V7XN3M9R6T1Y5W8ZA. |
label | string | yes | |
mode | enum: live, test | yes | live keys (mdh_live_) spend real credits; test keys (mdh_test_) return synthetic data and never charge. |
prefix | string | yes | The first characters of the secret, such as mdh_live_7Kq2. |
scopes | array of enum: account:read, counts:read, previews:read, audiences:read, audiences:write, quotes:read, orders:read, orders:write, and 8 more | yes | |
ip_allowlist | array of string | yes | CIDR ranges the key may be used from; empty allows any address. |
created_at | string (date-time) | yes | |
created_by | string | yes | Format: a user id: usr_ followed by a 26 character ULID in upper case Crockford base32, such as usr_01JA0Q3V7XN3M9R6T1Y5W8ZAK8. |
last_used_at | string (date-time) or null | yes | |
expires_at | string (date-time) or null | yes | |
revoked_at | string (date-time) or null | yes | |
kind | agent | yes | |
allowed_tools | array of enum: get_plans, get_account, list_filters, search_filter_values, expand_job_titles, resolve_location, compile_audience, count_audience, and 14 more | yes | The tools the key may call; a spending tool (create_order, enrich_contact) only if listed. |
allowed_connection_ids | array of string | yes | The CRM connections push_to_crm may push to live; empty allows dry runs only. |
monthly_budget_credits | integer (0 or more) | yes | |
per_order_max_credits | integer (0 or more) | yes | |
budget | Budget | yes | The live spend meter. |
budget.monthly_credits | integer (0 or more) | yes | |
budget.spent | number (0 or more) | yes | Credits, to one decimal place: whole at 1 credit a record, fractional only where a rule is (0.1 company-only, 1.8 outright, both proposed). |
budget.remaining | number (0 or more) | yes | Credits, to one decimal place: whole at 1 credit a record, fractional only where a rule is (0.1 company-only, 1.8 outright, both proposed). |
budget.resets_at | string (date-time) | yes | |
webhook_url | string (uri) or null | yes | |
disclosure | enum: account, anonymous | yes | account: counts as the organisation's own; anonymous: every anonymous visitor's rule, for a public assistant. |
Example response
{
"id": "key_01JB3Z9R6T1Y5W8ZAK8Q2V7XN3",
"kind": "agent",
"label": "SDR agent",
"mode": "live",
"prefix": "mdh_live_7Kq2",
"scopes": [
"counts:read",
"quotes:read",
"orders:write",
"orders:read",
"records:read",
"pushes:write"
],
"ip_allowlist": [],
"created_at": "2026-09-20T09:00:00Z",
"created_by": "usr_01JA0Q3V7XN3M9R6T1Y5W8ZAK8",
"last_used_at": "2026-09-26T02:00:00Z",
"expires_at": "2027-03-31T00:00:00Z",
"revoked_at": null,
"allowed_tools": [
"count_audience",
"quote_order",
"create_order",
"fetch_order_records",
"push_to_crm"
],
"allowed_connection_ids": ["conn_01JB3Y2X6Y7Z8A1B2C3D4E5F6G"],
"monthly_budget_credits": 2000,
"per_order_max_credits": 500,
"budget": {
"monthly_credits": 2000,
"spent": 400,
"remaining": 1600,
"resets_at": "2026-10-01T00:00:00Z"
},
"webhook_url": "https://agent.example.com/mydatahub/events",
"disclosure": "account"
}Errors
Every error is a problem object with a code and a next_step. Every response carries the RateLimit and RateLimit-Policy headers; see Rate limits.
| Status | Problem codes | When |
|---|---|---|
| 400 | invalid-request, idempotency-key-required, unsupported-version | The request could not be read, or an Idempotency-Key is missing. |
| 401 | authentication-required, account-required | Sign in (OAuth) or send an API key (authentication-required), or create a free account for what the anonymous tier does not cover (account-required). |
| 403 | insufficient-scope, confirmation-required, business-details-required, licence-acceptance-required, dpa-acceptance-required, agent-budget-exceeded | The token lacks a scope (insufficient-scope, with required_scope and a WWW-Authenticate step-up challenge); no person confirmed the spend and no agent key covers it (confirmation-required, with an approval_url); an agent key's per-order cap or monthly budget does not cover it (agent-budget-exceeded); an account made by self-serve sign-up has not yet confirmed its business name and that it is a UK business, which its first order needs (business-details-required); the order needs the Data Licence and Sharing Agreement accepted at the version in force (licence-acceptance-required); or a suppression upload comes before the organisation has accepted the Article 28 data processing agreement in force (dpa-acceptance-required). Nothing is charged. |
| 404 | not-found | No such object for this organisation. |
| 422 | validation-failed, quote-required, dataset-unavailable | The request is not valid. errors lists every problem with a JSON pointer and did_you_mean. |
| 429 | rate-limited | Too many requests. Wait for Retry-After. |
| Any other failure | internal-error, service-unavailable, tps-screen-expired | Something failed on our side (500 internal-error) or the service is briefly unavailable (503 service-unavailable, with Retry-After). Nothing was charged; retry with the same Idempotency-Key. A count, sample, quote or order with telephone in play is also refused with 503 tps-screen-expired, with no Retry-After, while the dataset version's TPS and CTPS screen is more than 28 days old. |