---
title: "Update an account's metadata or connection location"
method: PATCH
path: "/v1/accounts/{account_id}"
tags: ["Accounts"]
---

# Update an account's metadata or connection location

`PATCH /v1/accounts/{account_id}`

## Request

- Base URL: `https://api.curviate.com`
- URL: `https://api.curviate.com/v1/accounts/{account_id}`
- Auth: HTTP bearer

Updates metadata, external_id, the connection location (country) or your own proxy (null plus a country returns to a managed location). Returns the account; connection_location is re-read after a location change.

## Path parameters

- `account_id` string, required

## Request body

- object
  - `metadata` object, nullable: Your own flat string map for this account (up to 16 keys, key 40 chars, value 500). Replaces the stored map whole; null clears it.
  - `external_id` string, nullable: Your own id for this account's end user (1-255 chars), or null to clear it. Omit to leave it unchanged.
  - `proxy` object, nullable: Your own proxy, or null to stop using it and return to a managed location (null requires country in the same request). Omit to leave it unchanged.
    - `protocol` 'http' | 'https' | 'socks5' | 'socks4', required: Proxy protocol. One of http, https, socks5, socks4.
    - `host` string, required: Proxy host or IP.
    - `port` integer, required: Proxy port (1-65535).
    - `username` string: Proxy auth username (optional).
    - `password` string: Proxy auth password (optional). Passed to the connection only: never stored, logged or returned.
  - `country` 'AE' | 'AO' | 'AR' | 'AT' | 'AU' | 'BD' | 'BE' | 'BG' | 'BR' | 'CA' | 'CH' | 'CL' | 'CN' | 'CO' | 'CY' | 'CZ' | 'DE' | 'DK' | 'EE' | 'EG' | 'ES' | 'FI' | 'FR' | 'GB' | 'GT' | 'HK' | 'ID' | 'IE' | 'IL' | 'IN' | 'IT' | 'JP' | 'KR' | 'KZ' | 'MA' | 'MX' | 'MY' | 'NL' | 'NO' | 'PA' | 'PH' | 'PK' | 'PL' | 'PR' | 'PT' | 'RO' | 'SA' | 'SE' | 'SG' | 'TH' | 'TN' | 'TR' | 'UA' | 'US' | 'VN' | 'ZA': Move this account's connection location to this supported ISO 3166-1 alpha-2 country (e.g. US, DE). LinkedIn sees a new IP in that country and may ask the owner to verify, so change it only when the current location is wrong. Strict unless allow_country_fallback is true. On an account using your own proxy, send proxy: null with it.
  - `allow_country_fallback` boolean: Optional. false (the default with country or ip): only ever connect from that country; a connect fails when no connection is free there. true: a connection from another country may be used when none is free in the chosen one. Not accepted with proxy.

## Response `200`

The updated account: the object GET /v1/accounts/{account_id} returns, without quotas and event_log (read those on GET). After a location change, connection_location is re-read from LinkedIn's side, so it shows where the account really connects from now. metadata and external_id are stored by Curviate; a PATCH carrying only those makes no LinkedIn call.

- object
  - `object` 'account': Response type discriminator.
  - `account_id` string
  - `status` 'active' | 'reconnect_needed' | 'restricted' | 'connecting' | 'disconnected'
  - `auth_method` string
  - `full_name` string, nullable
  - `headline` string, nullable
  - `connected_at` string, nullable: ISO-8601 UTC connection timestamp.
  - `last_checked_at` string: ISO-8601 UTC time the account state was last checked.
  - `seat_id` string, nullable: The seat this account occupies (null for an admin seatless account).
  - `requested_products` string[], nullable: The connection scope this account was last connected with (e.g. ["classic","company","sales_navigator","recruiter"]). A connect asks for every product and LinkedIn activates the ones the account actually has, so this is what was ASKED for, narrowed only by an explicit linkedin_premium on that connect. A linkedin_premium narrowing is not remembered between connects, so this shows what the LAST connect asked for and a reconnect that omits the field widens it again. Null for accounts connected before this was recorded; not attachment truth for Company Pages.
  - `external_id` string, nullable: Your own id for this account's end user, as set on connect or PATCH. Null when none. Not unique.
  - `metadata` object, nullable: Your own flat string map for this account, as last set by PATCH. Null when none.
  - `connection_location` object: Where LinkedIn sees this account connecting from. country: the configured country (ISO 3166-1 alpha-2), the one the account is set to use. current_country: the country of the connection in use right now; it differs from country only after a fallback, which can happen only when strict is false. mode: auto (a managed connection in country) or custom (your own proxy). strict: true when the account never connects from another country, null when unknown. Each field is null until known; an account connected through ip or your own proxy may show null until the next background refresh. Change it with PATCH /v1/accounts/{account_id} (country), or by reconnecting with country.
    - `country` string, nullable: Configured country (ISO 3166-1 alpha-2).
    - `current_country` string, nullable: Country of the connection in use right now.
    - `mode` 'auto' | 'custom' | 'null', nullable: auto: managed connection. custom: your own proxy.
    - `strict` boolean, nullable: true: never connects from another country.
  - `substrate_created_at` string, nullable: ISO-8601 UTC creation timestamp of the underlying LinkedIn account, distinct from connected_at. Null until the first background enrichment lands.
  - `account_states` string[]: Platform conditions this LinkedIn account is currently in, additive to `status` and independent of it. `recruiter_session_evicted`: the account is signed in somewhere else and LinkedIn allows one session at a time for it, so a person has to close the other session; reconnecting will not help. `commercial_use_limited`: LinkedIn's commercial-use limit has tripped, so people search returns only a handful of results per query. It is a paywall, not a penalty, and nothing else about the account is affected. `profile_view_limited`: profile views outside this account's own network are being blocked; first-degree views still work. The two limits are detected from their symptom: LinkedIn publishes no threshold for either, exposes no counter, and lifts neither on request, so no number is reported. `commercial_use_limited` is a present-tense reading and a genuinely narrow search can also produce the shape it looks for, so treat it as a reason to check rather than a certainty; the next people search that returns a full page clears it. An account in none of these reports an empty array.
  - `activity_window` object: The account's activity window, the hours in which Curviate reports or refuses live writes to LinkedIn. Reads and anything served from Curviate's own store are never affected, at any hour. Configure it on the account's safety policy.
    - `timezone` string, nullable: The IANA zone the window is read in, or null when none is set.
    - `timezone_unset` boolean: True when no zone is set, which DISABLES the activity window. Curviate never guesses a zone: a guess eight hours wrong inverts the window, refusing every daytime write and permitting every nocturnal one. If you configured a window and it never fires, this is why.
  - `safety_warning` SafetyWarning: Present on a successful response when this account crossed an account-safety ceiling while the affected budget row is on the default warn posture. The action still happened. Field-identical to the BUDGET_EXHAUSTED error body except that blocked is false and hint.message describes the outcome, so one branch handles both postures and switching the account to enforce is a configuration change rather than a client rewrite.
    - `row` string, nullable, required: The budget row that breached, for example profile_views or pending_invites. Null when the breach is not about a row: an activity_window warning on an action that spends no budget of its own (editing a message, dismissing a notification, a Recruiter or Sales Navigator admin action) is still governed by the hours the account works in. Read hint.parameter for what to change; whenever this is null it names posture, because the activity window edges are set per action type and this action has none, so the levers that reach it are the account timezone and posture.
    - `reset_at` string, nullable, required: The instant the refusal lifts: the window roll on a ceiling refusal, the next window open on an activity-window one. Null in the two cases no clock frees, the pending_invites gauge and InMail credits (row inmail).
    - `hint` object, required
      - `parameter` string, required: The settable parameter on PATCH /v1/{account_id}/safety-policy.
      - `message` string, required: What the condition is and what changing that parameter would do. This is the one field whose text differs from the BUDGET_EXHAUSTED refusal: here it says the action went through, and on a ceiling breach how far past the ceiling this row now stands, rather than telling you to set posture to warn when it already is. Branch on parameter, reason and blocked, never on this text.
    - `reason` 'ceiling' | 'activity_window', required: Which rule was crossed. ceiling means the row's configured limit is spent; activity_window means the account is outside the hours it works in, which is a different fact with a different remedy. hint.parameter names the right setting for each.
    - `blocked` boolean, required: Always false here. True on the BUDGET_EXHAUSTED refusal carrying the same payload.

## Other responses

- `400`: INVALID_REQUEST: a malformed proxy object; proxy together with country or allow_country_fallback; country on an account that uses your own proxy without proxy: null; allow_country_fallback alone on such an account; an unsupported country; ip (not changeable after connect; reconnect with ip instead); metadata over its limits (16 keys, key 40 chars, value 500); external_id outside 1-255 chars. CONNECTION_LOCATION_REQUIRED: proxy: null without country (clearing your own proxy alone would let the connection land anywhere).
- `404`: No such account for this tenant. An id whose connection was replaced or removed also stops resolving here, and it is already absent from the list, so re-read `GET /v1/accounts` for the current id.
- `409`: REAUTH_REQUIRED: the account is disconnected, so its location cannot change in place. Reconnect it (POST /v1/auth/intent with account_id) and pass country there.
- `413`: Payload too large, the request body exceeds the size limit.
- `422`: CONNECTION_LOCATION_UNAVAILABLE: no connection is free in that country right now, or the account did not end up there. When the change was applied but landed elsewhere, the body carries connection_location with where the account connects from now. Try a nearby country, or allow fallback.
- `429`: Rate limited, slow down and retry after the hinted delay.
- `500`: Internal error.
- `501`: This proxy configuration is not supported for this account (e.g. the requested proxy mode is unavailable on the account's current plan). Not retryable as-is, change the request rather than resubmitting.
- `502`: A temporary error occurred. Please try again.
- `503`: Service unavailable.
- `504`: Gateway timeout.

---

[API](https://skmtc.dev/curviate/apis/curviate-api.md) · [All operations](https://skmtc.dev/curviate/apis/curviate-api/llms.txt) · [OpenAPI document](https://skmtc.dev/curviate/apis/curviate-api/revisions/eab848801d95?raw)
