# Accept the data processing agreement

> POST /v1/account/agreements. Accept the Article 28 data processing agreement at the version in force (the account's agreements.dpa).

Source: https://emailmovers.com/mydatahub/docs/reference/rest/accept-account-agreements

`POST /v1/account/agreements`

Accept the Article 28 data processing agreement at the version in force (the account's agreements.dpa). A suppression upload needs it first: until it is accepted POST /v1/suppressions answers 403 dpa-acceptance-required and uploads nothing, unless a person accepts it with that upload. A signed-in person who owns the account accepts it, through OAuth (with account:write, or admin) or in the app's Settings; an API key cannot. The account keeps the version, the person and the time, and a later version is accepted again.

| Property | Value |
| --- | --- |
| Endpoint | `POST https://mydatahub.emailmovers.com/v1/account/agreements` |
| Operation id | `acceptAccountAgreements` |
| Cost | Free (0 credits) |
| Access | A signed-in person only: an OAuth 2.1 token with the scope `account:write`. API keys are refused. |
| Tag | Account |

> **Not yet live.** MyDataHub v2 is built and tested locally and is not yet live. The v2 hosts open at launch, date to confirm. This page describes the contract the API is tested against.

## Request

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `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 |
| --- | --- | --- | --- |
| `dpa_accepted_version` | string | yes | The version of the Article 28 data processing agreement the owner accepts: the version in force, the account's agreements.dpa.version. |

## Example request

```bash
curl -X POST "https://mydatahub.emailmovers.com/v1/account/agreements" \
  -H "Authorization: Bearer $MYDATAHUB_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "dpa_accepted_version": "1.0"
}'
```

## Responses

### 200: The account, with its agreements.

Headers: `RateLimit`, `RateLimit-Policy`.

| Member | Type | Always present | Description |
| --- | --- | --- | --- |
| `organisation` | object | yes |  |
| `organisation.id` | string | yes | Format: an organisation id: org_ followed by a 26 character ULID in upper case Crockford base32, such as org_01JA0Q2V7XN3M9R6T1Y5W8ZAK8. |
| `organisation.name` | string | yes |  |
| `organisation.business_details` | `BusinessDetails` | yes | Whether the business name and a UK business confirmation are on record, which a self-serve account needs before its first order. |
| `user` | object or null | yes | The signed-in person; null when the caller is an API key. |
| `user.id` | string | yes | Format: a user id: usr_ followed by a 26 character ULID in upper case Crockford base32, such as usr_01JA0Q3V7XN3M9R6T1Y5W8ZAK8. |
| `user.name` | string | yes |  |
| `mode` | enum: `live`, `test` | yes | live keys (mdh_live_) spend real credits; test keys (mdh_test_) return synthetic data and never charge. |
| `plan` | object | yes |  |
| `plan.id` | string | yes |  |
| `plan.name` | string | yes |  |
| `plan.kind` | enum: `free`, `subscription`, `pack` | yes |  |
| `plan.renews_at` | string (date) or null | yes |  |
| `plan.cancel_at_period_end` | boolean | yes |  |
| `balance` | object | yes | Credits by source. |
| `balance.credits` | number | yes | Credits available to spend now. |
| `balance.pack_credits` | number | yes | From credit packs: never expire. |
| `balance.subscription_credits` | number | yes | From the plan: roll over while subscribed, up to the cap. |
| `balance.rollover_cap` | integer (0 or more) or null | yes |  |
| `balance.low_threshold` | integer (0 or more) or null | yes | The credits.low webhook fires when the balance crosses this. |
| `prices` | array of `CreditRule` | yes | What each action costs in credits. |
| `prices[].action` | string | yes |  |
| `prices[].label` | string | yes |  |
| `prices[].credits` | 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). |
| `prices[].unit` | enum: `call`, `record`, `match` | yes |  |
| `prices[].status` | enum: `confirmed`, `proposed`, `to_confirm` | yes | confirmed is in force; proposed awaits a decision; to_confirm is a figure still being checked. |
| `prices[].note` | string | no |  |
| `gbp_per_credit` | number (0 or more) | yes | Your plan's price per credit, excluding VAT. |
| `rate_limit` | `RateLimit` | yes | The caller's count limits and what remains in the window. |
| `rate_limit.tier` | enum: `anonymous`, `free`, `paid` | yes |  |
| `rate_limit.counts_per_minute` | integer (0 or more) or null | yes |  |
| `rate_limit.counts_per_hour` | integer (0 or more) or null | yes |  |
| `rate_limit.counts_per_day` | integer (0 or more) or null | yes |  |
| `rate_limit.remaining` | integer (0 or more) | yes |  |
| `rate_limit.resets_at` | string (date-time) | yes |  |
| `rate_limit.status` | enum: `confirmed`, `to_confirm` | yes |  |
| `key` | object or null | yes | The calling key and, for an agent key, its budget; null for a person. |
| `key.id` | string | yes | Format: an API key id: key_ followed by a 26 character ULID in upper case Crockford base32, such as key_01JB3Z4Q2V7XN3M9R6T1Y5W8ZA. |
| `key.kind` | enum: `standard`, `agent` | yes |  |
| `key.budget` | `Budget` or null | yes |  |
| `key.per_order_max_credits` | integer (0 or more) or null | yes |  |
| `key.expires_at` | string (date-time) or null | yes |  |
| `account_manager` | object or null | yes | The named person behind Talk to us. |
| `account_manager.name` | string | yes |  |
| `account_manager.calendar_url` | string (uri) | yes |  |
| `agreements` | object | yes | The Data Licence and Sharing Agreement (accepted with every order, or with an agent key that orders on its own) and the Article 28 DPA (accepted with the first suppression list). |
| `agreements.dlsa` | `Agreement` | yes |  |
| `agreements.dpa` | `Agreement` | yes |  |

#### Example response

```json
{
  "organisation": {
    "id": "org_01JA0Q2V7XN3M9R6T1Y5W8ZAK8",
    "name": "Example Agency Ltd",
    "business_details": {
      "confirmed_at": null,
      "required_before_order": false,
      "name_editable": false
    }
  },
  "user": null,
  "mode": "live",
  "plan": {
    "id": "pay_as_you_go",
    "name": "Pay as you go",
    "kind": "subscription",
    "renews_at": "2026-10-26",
    "cancel_at_period_end": false
  },
  "balance": {
    "credits": 6200,
    "pack_credits": 5000,
    "subscription_credits": 1200,
    "rollover_cap": 12000,
    "low_threshold": 500
  },
  "prices": [
    {
      "action": "count",
      "label": "Count, breakdown, preview, machine quote, catalogue, title expansion",
      "credits": 0,
      "unit": "call",
      "status": "confirmed"
    },
    {
      "action": "record",
      "label": "Full UK contact record delivered, any channel or field set",
      "credits": 1,
      "unit": "record",
      "status": "confirmed"
    },
    {
      "action": "company_only_record",
      "label": "Company-only record (no named contact)",
      "credits": 0.1,
      "unit": "record",
      "status": "proposed"
    },
    {
      "action": "enrich_contact",
      "label": "Contact enrichment, match found",
      "credits": 1,
      "unit": "match",
      "status": "confirmed",
      "note": "0 if already licensed"
    },
    {
      "action": "lookup_company",
      "label": "Company lookup, no contact details",
      "credits": 0,
      "unit": "call",
      "status": "to_confirm"
    },
    {
      "action": "outright",
      "label": "Outright ownership",
      "credits": 1.8,
      "unit": "record",
      "status": "proposed"
    }
  ],
  "gbp_per_credit": 0.299,
  "rate_limit": {
    "tier": "paid",
    "counts_per_minute": 600,
    "counts_per_hour": null,
    "counts_per_day": null,
    "remaining": 598,
    "resets_at": "2026-09-26T10:15:00Z",
    "status": "to_confirm"
  },
  "key": {
    "id": "key_01JB3Z9R6T1Y5W8ZAK8Q2V7XN3",
    "kind": "agent",
    "budget": {
      "monthly_credits": 1000,
      "spent": 400,
      "remaining": 600,
      "resets_at": "2026-10-01T00:00:00Z"
    },
    "per_order_max_credits": 500,
    "expires_at": "2027-03-31T00:00:00Z"
  },
  "account_manager": {
    "name": "Jordan Hale",
    "calendar_url": "https://emailmovers.com/talk-to-us"
  },
  "agreements": {
    "dlsa": {
      "name": "Data Licence and Sharing Agreement",
      "version": "1.0",
      "url": "https://emailmovers.com/data-licence/",
      "accepted": {
        "version": "1.0",
        "accepted_at": "2026-11-02T10:12:00Z",
        "accepted_by": "usr_01JB4H8Q2V7XN3M9R6T1Y5W8ZA"
      }
    },
    "dpa": {
      "name": "Article 28 data processing agreement",
      "version": "1.0",
      "url": "https://emailmovers.com/dpa/",
      "accepted": {
        "version": "1.0",
        "accepted_at": "2026-11-03T09:00:00Z",
        "accepted_by": "usr_01JB4H8Q2V7XN3M9R6T1Y5W8ZA"
      }
    }
  }
}
```

## 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](https://emailmovers.com/mydatahub/docs/rate-limits.md).

| Status | Problem codes | When |
| --- | --- | --- |
| 400 | [`invalid-request`](https://emailmovers.com/mydatahub/docs/errors/invalid-request.md), [`idempotency-key-required`](https://emailmovers.com/mydatahub/docs/errors/idempotency-key-required.md), [`unsupported-version`](https://emailmovers.com/mydatahub/docs/errors/unsupported-version.md) | The request could not be read, or an Idempotency-Key is missing. |
| 401 | [`authentication-required`](https://emailmovers.com/mydatahub/docs/errors/authentication-required.md), [`account-required`](https://emailmovers.com/mydatahub/docs/errors/account-required.md) | 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`](https://emailmovers.com/mydatahub/docs/errors/insufficient-scope.md), [`confirmation-required`](https://emailmovers.com/mydatahub/docs/errors/confirmation-required.md), [`business-details-required`](https://emailmovers.com/mydatahub/docs/errors/business-details-required.md), [`licence-acceptance-required`](https://emailmovers.com/mydatahub/docs/errors/licence-acceptance-required.md), [`dpa-acceptance-required`](https://emailmovers.com/mydatahub/docs/errors/dpa-acceptance-required.md), [`agent-budget-exceeded`](https://emailmovers.com/mydatahub/docs/errors/agent-budget-exceeded.md) | 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. |
| 422 | [`validation-failed`](https://emailmovers.com/mydatahub/docs/errors/validation-failed.md), [`quote-required`](https://emailmovers.com/mydatahub/docs/errors/quote-required.md), [`dataset-unavailable`](https://emailmovers.com/mydatahub/docs/errors/dataset-unavailable.md) | The request is not valid. errors lists every problem with a JSON pointer and did_you_mean. |
| 429 | [`rate-limited`](https://emailmovers.com/mydatahub/docs/errors/rate-limited.md) | Too many requests. Wait for Retry-After. |
| Any other failure | [`internal-error`](https://emailmovers.com/mydatahub/docs/errors/internal-error.md), [`service-unavailable`](https://emailmovers.com/mydatahub/docs/errors/service-unavailable.md), [`tps-screen-expired`](https://emailmovers.com/mydatahub/docs/errors/tps-screen-expired.md) | 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. |
