More

Legacy API (v1)

The first version of the API keeps working for existing integrations. New integrations should use V2, which adds idempotent creates, your own client IDs, cancellation, deadlines, webhooks and consistent errors.

This API receives no new features. Use the same API keys with V2 to get everything below and more.

Authentication

Send your API key in the x-auth header. Base URL: https://api.agencyaccess.co/api. Each endpoint needs the scope shown. Responses are { "error": false, "data": ... }, or { "error": true, "msg": "..." } with an HTTP error status: 403 without a key or when your plan has no API access, 401 for an invalid or revoked key, and 401 when the key lacks the endpoint's scope. Creating a link past your plan's monthly limit returns 403. Each IP address can make 300 calls a minute; over that, calls return 429 with { "status": "fail", "message": "..." }.

POST /clients/create

Creates a client. name, email and language (English, Spanish or Dutch) are required; company, website and metadata (string values) are optional. Returns 201 with the new client ID. Scope clients:create.

Request
curl -X POST "https://api.agencyaccess.co/api/clients/create" \
  -H "x-auth: $AGENCYACCESS_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "name": "Jamie Rivera",
  "email": "jamie@northwindcoffee.com",
  "language": "English",
  "company": "Northwind Coffee",
  "metadata": {
    "crmId": "4821"
  }
}
JSON
Response
{
  "error": false,
  "data": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17"
}

POST /clients/update

Updates a client by id, the only required field. Other fields you send replace the current values (metadata as a whole); fields you leave out or send empty keep their values, except metadata, which an empty object clears. Returns the client ID. Scope clients:update.

Request
curl -X POST "https://api.agencyaccess.co/api/clients/update" \
  -H "x-auth: $AGENCYACCESS_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "id": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
  "name": "Jamie Rivera",
  "email": "jamie@northwindcoffee.com",
  "language": "English"
}
JSON
Response
{
  "error": false,
  "data": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17"
}

GET /clients/retrieve?id={id}

Returns a client. Scope clients:read.

Request
curl "https://api.agencyaccess.co/api/clients/retrieve?id=7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17" \
  -H "x-auth: $AGENCYACCESS_API_KEY"
Response
{
  "error": false,
  "data": {
    "id": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
    "name": "Jamie Rivera",
    "email": "jamie@northwindcoffee.com",
    "...": "..."
  }
}

Creates an access link for clientID. requestedServices maps service names to { requested: true, accessLevel, requestedAccountLinks: [{ internalAccountID }], optional? }; at least one service is required, services without requested: true are not added (but must still be valid service names and roles), and Google Ads MCC links also need googleAdsMCCAccountID. Optional: intakeForm (boolean; 409 if your intake form is disabled), sendEmail, thankYouMessage, redirectURL (a URL without https://, such as example.com/thanks). Returns 201 with the link ID and inviteUrl. Scope requests:create.

Request
curl -X POST "https://api.agencyaccess.co/api/links/create" \
  -H "x-auth: $AGENCYACCESS_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "clientID": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
  "requestedServices": {
    "Google Ads": {
      "requested": true,
      "accessLevel": "ADMIN",
      "requestedAccountLinks": [
        {
          "internalAccountID": "Xk7pQ2mR9vT4wB8nC3dF6hJ1"
        }
      ]
    }
  },
  "sendEmail": false
}
JSON
Response
{
  "error": false,
  "data": "2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
  "inviteUrl": "https://acme.agencyaccess.co/i/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72"
}

Replaces the services of an open access link (id, requestedServices, same shape as create). Send the full set: services you leave out are removed, and a service keeps its progress only if its role and accounts are unchanged. Completed, cancelled and expired links return 409. Returns the link ID. Scope requests:update.

Request
curl -X POST "https://api.agencyaccess.co/api/links/update" \
  -H "x-auth: $AGENCYACCESS_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "id": "2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
  "requestedServices": {
    "Google Ads": {
      "requested": true,
      "accessLevel": "ADMIN",
      "requestedAccountLinks": [
        {
          "internalAccountID": "Xk7pQ2mR9vT4wB8nC3dF6hJ1"
        }
      ]
    },
    "Google Analytics": {
      "requested": true,
      "accessLevel": "predefinedRoles/viewer",
      "requestedAccountLinks": [
        {
          "internalAccountID": "Xk7pQ2mR9vT4wB8nC3dF6hJ1"
        }
      ]
    }
  }
}
JSON
Response
{
  "error": false,
  "data": "2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72"
}

Returns an access link with its status (which reports expired once the deadline passes) and requestedServices, keyed by service name. Scope requests:read.

Request
curl "https://api.agencyaccess.co/api/links/retrieve?id=2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72" \
  -H "x-auth: $AGENCYACCESS_API_KEY"
Response
{
  "error": false,
  "data": {
    "id": "2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
    "clientID": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
    "status": "in_progress",
    "requestedServices": {
      "Google Ads": {
        "requested": true,
        "accessLevel": "ADMIN",
        "granted": true,
        "...": "..."
      }
    },
    "...": "..."
  }
}

Moving to V2

LegacyV2
POST /api/clients/createPOST /api/v2/clients
POST /api/clients/updatePATCH /api/v2/clients/{id}
GET /api/clients/retrieveGET /api/v2/clients/{id}
POST /api/links/createPOST /api/v2/requests
GET /api/links/retrieveGET /api/v2/requests/{id}

In V2, send the key as Authorization: Bearer ..., use camelCase fields (clientId, internalAccountId, redirectUrl) and add an Idempotency-Key header to creates. V2 rejects fields it doesn't know, so drop requested from each service and rename googleAdsMCCAccountID to googleAdsMccAccountId. /api/links/update has no V2 equivalent: cancel the request and create a new one.