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.
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 {
"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.
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 {
"error": false,
"data": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17"
} GET /clients/retrieve?id={id}
Returns a client. Scope clients:read.
curl "https://api.agencyaccess.co/api/clients/retrieve?id=7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17" \
-H "x-auth: $AGENCYACCESS_API_KEY" {
"error": false,
"data": {
"id": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
"name": "Jamie Rivera",
"email": "jamie@northwindcoffee.com",
"...": "..."
}
} POST /links/create
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.
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 {
"error": false,
"data": "2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
"inviteUrl": "https://acme.agencyaccess.co/i/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72"
} POST /links/update
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.
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 {
"error": false,
"data": "2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72"
} GET /links/retrieve?id={id}
Returns an access link with its status (which reports expired once the deadline passes) and requestedServices, keyed by service name. Scope requests:read.
curl "https://api.agencyaccess.co/api/links/retrieve?id=2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72" \
-H "x-auth: $AGENCYACCESS_API_KEY" {
"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
| Legacy | V2 |
|---|---|
POST /api/clients/create | POST /api/v2/clients |
POST /api/clients/update | PATCH /api/v2/clients/{id} |
GET /api/clients/retrieve | GET /api/v2/clients/{id} |
POST /api/links/create | POST /api/v2/requests |
GET /api/links/retrieve | GET /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.