EmailmoversMyDataHub docs
Browse the docs

Reference / REST reference

Reference

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).

Generated from the contractsView as Markdown

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.

PropertyValue
EndpointPOST https://mydatahub.emailmovers.com/v1/account/agreements
Operation idacceptAccountAgreements
CostFree (0 credits)
AccessA signed-in person only: an OAuth 2.1 token with the scope account:write. API keys are refused.
TagAccount

Request

Parameters

NameInTypeRequiredDescription
MyDataHub-VersionheaderstringnoThe 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

MemberTypeRequiredDescription
dpa_accepted_versionstringyesThe version of the Article 28 data processing agreement the owner accepts: the version in force, the account's agreements.dpa.version.

Example request

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.

MemberTypeAlways presentDescription
organisationobjectyes
organisation.idstringyesFormat: an organisation id: org_ followed by a 26 character ULID in upper case Crockford base32, such as org_01JA0Q2V7XN3M9R6T1Y5W8ZAK8.
organisation.namestringyes
organisation.business_detailsBusinessDetailsyesWhether the business name and a UK business confirmation are on record, which a self-serve account needs before its first order.
userobject or nullyesThe signed-in person; null when the caller is an API key.
user.idstringyesFormat: a user id: usr_ followed by a 26 character ULID in upper case Crockford base32, such as usr_01JA0Q3V7XN3M9R6T1Y5W8ZAK8.
user.namestringyes
modeenum: live, testyeslive keys (mdh_live_) spend real credits; test keys (mdh_test_) return synthetic data and never charge.
planobjectyes
plan.idstringyes
plan.namestringyes
plan.kindenum: free, subscription, packyes
plan.renews_atstring (date) or nullyes
plan.cancel_at_period_endbooleanyes
balanceobjectyesCredits by source.
balance.creditsnumberyesCredits available to spend now.
balance.pack_creditsnumberyesFrom credit packs: never expire.
balance.subscription_creditsnumberyesFrom the plan: roll over while subscribed, up to the cap.
balance.rollover_capinteger (0 or more) or nullyes
balance.low_thresholdinteger (0 or more) or nullyesThe credits.low webhook fires when the balance crosses this.
pricesarray of CreditRuleyesWhat each action costs in credits.
prices[].actionstringyes
prices[].labelstringyes
prices[].creditsnumber (0 or more)yesCredits, 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[].unitenum: call, record, matchyes
prices[].statusenum: confirmed, proposed, to_confirmyesconfirmed is in force; proposed awaits a decision; to_confirm is a figure still being checked.
prices[].notestringno
gbp_per_creditnumber (0 or more)yesYour plan's price per credit, excluding VAT.
rate_limitRateLimityesThe caller's count limits and what remains in the window.
rate_limit.tierenum: anonymous, free, paidyes
rate_limit.counts_per_minuteinteger (0 or more) or nullyes
rate_limit.counts_per_hourinteger (0 or more) or nullyes
rate_limit.counts_per_dayinteger (0 or more) or nullyes
rate_limit.remaininginteger (0 or more)yes
rate_limit.resets_atstring (date-time)yes
rate_limit.statusenum: confirmed, to_confirmyes
keyobject or nullyesThe calling key and, for an agent key, its budget; null for a person.
key.idstringyesFormat: an API key id: key_ followed by a 26 character ULID in upper case Crockford base32, such as key_01JB3Z4Q2V7XN3M9R6T1Y5W8ZA.
key.kindenum: standard, agentyes
key.budgetBudget or nullyes
key.per_order_max_creditsinteger (0 or more) or nullyes
key.expires_atstring (date-time) or nullyes
account_managerobject or nullyesThe named person behind Talk to us.
account_manager.namestringyes
account_manager.calendar_urlstring (uri)yes
agreementsobjectyesThe 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.dlsaAgreementyes
agreements.dpaAgreementyes

Example response

{
  "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.

StatusProblem codesWhen
400invalid-request, idempotency-key-required, unsupported-versionThe request could not be read, or an Idempotency-Key is missing.
401authentication-required, account-requiredSign in (OAuth) or send an API key (authentication-required), or create a free account for what the anonymous tier does not cover (account-required).
403insufficient-scope, confirmation-required, business-details-required, licence-acceptance-required, dpa-acceptance-required, agent-budget-exceededThe 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.
422validation-failed, quote-required, dataset-unavailableThe request is not valid. errors lists every problem with a JSON pointer and did_you_mean.
429rate-limitedToo many requests. Wait for Retry-After.
Any other failureinternal-error, service-unavailable, tps-screen-expiredSomething 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.