API reference
Requests
Access requests: the link a client opens to grant your agency access to their accounts, and the progress of each requested service.
Create a request
POST/requests Creates an access request for a client and returns its inviteUrl (to send or link to) and embedUrl (to show in an iframe). No email is sent unless sendEmail is true. Each request counts once toward your monthly access link allowance (prospects separately); at an enforced limit you get 403 PLAN_LIMIT_REACHED, and with overage billing extra client links are billed instead. Request at least one service or the intake form.
Headers
-
Idempotency-Keystring requiredA unique value per create, such as a UUID (1-255 visible ASCII characters). Retrying with the same key and body returns the original result instead of creating another object; keys are kept for 24 hours.
Body
-
clientIdstring requiredThe client to request access from.
-
requestedServicesobjectServices to request, keyed by service ID (see
GET /services).Show fields of requestedServices
-
accessLevelstringOne of the service's role values; leave out for services without roles.
-
optionalbooleanThe client may skip it. The request completes once each service is granted, or skipped if optional. Default
false. -
requestedAccountLinksarray of objectWhich of your connected accounts (see
GET /accounts) the client grants access to. Each must be one of your accounts on the service's platform.Show fields of requestedAccountLinks
-
internalAccountIdstring required -
googleAdsMccAccountIdstringRequired for Google Ads MCC.
-
metaBusinessManagerIdstring
-
-
-
intakeFormobjectAsk the client to fill in your intake form (it must be enabled with questions).
Show fields of intakeForm
-
requestedboolean required
-
-
sendEmailbooleanEmail the client the link. Default
false. -
thankYouMessagestringUp to 2000 characters.
-
redirectUrlstringAn absolute http(s) URL to send the client to after finishing. Up to 2048 characters.
-
expiresAtdatetimeA future time, at most a year away, after which the client can no longer grant access. Without it the request never expires.
Returns
201 The new request.
Fields (object)
-
iduuid -
clientIduuid -
externalClientIdstring | null -
statusstringpending: nothing granted, skipped or submitted yet (opening the link or signing in does not change it).in_progress: at least one service is granted or skipped, an invite awaits your confirmation, or the intake form is submitted.completed: every service is granted (optional ones may be skipped instead), not every service was skipped, and the intake form, if requested, is submitted. A completed request returns toin_progressif you ask for more access.cancelledandexpiredare final. One ofpending,in_progress,completed,cancelled,expired. -
stateVersionintegerIncreases with every change; use it to ignore older states and events.
-
sourcestringWhere the request was created. One of
api,dashboard,static_link. -
inviteUrlstringThe link the client opens to grant access.
-
embedUrlstringThe same link for an iframe in your portal.
-
intakeFormobjectShow fields of intakeForm
-
requestedboolean -
completeboolean
-
-
servicesarray of objectShow fields of services
-
servicestringThe service ID, such as
Google Ads. -
platformstringThe platform, such as
Google. -
accessLevelstring | nullThe requested role.
-
optionalbooleanThe client may skip it. Optional services still need to be granted or skipped for the request to complete.
-
statusstringOnly optional services can be skipped. One of
pending,granted,skipped. -
accountsarray of objectYour accounts access is requested for.
Show fields of accounts
-
internalAccountIdstring -
googleAdsMccAccountIdstring | null -
metaBusinessManagerIdstring | null
-
-
grantedAssetsarray of objectThe client's assets access was granted to.
Show fields of grantedAssets
-
idstringThe asset's ID on the platform.
-
namestring | null -
accessLevelstring | null
-
-
-
thankYouMessagestring | null -
redirectUrlstring | nullWhere the client is sent after finishing.
-
createdAtdatetime -
completedAtdatetime | null -
expiresAtdatetime | nullAfter this, no new grants; the request reports
expiredunless it is already completed or cancelled. Null when the request never expires. -
cancelledAtdatetime | null
- 400 The request is invalid (
VALIDATION_FAILED);detailslists the problems, withfieldwhen one applies. - 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 404 Not found, or it belongs to another agency.
- 409 A conflict: an
externalClientIdin use or immutable, an Idempotency-Key reused with a different body or still in progress, or a request in a state that does not allow this. - 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl -X POST "https://api.agencyaccess.co/api/v2/requests" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"clientId": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
"requestedServices": {
"Google Ads": {
"accessLevel": "ADMIN",
"requestedAccountLinks": [
{
"internalAccountId": "Xk7pQ2mR9vT4wB8nC3dF6hJ1"
}
]
},
"Meta Ads": {
"accessLevel": "['ADVERTISE', 'ANALYZE']",
"optional": true,
"requestedAccountLinks": [
{
"internalAccountId": "Lm5sZ8aE2gH6jK9qN3rU7yW4"
}
]
}
},
"intakeForm": {
"requested": true
},
"redirectUrl": "https://portal.example.com/onboarding/done"
}
JSON const res = await fetch("https://api.agencyaccess.co/api/v2/requests", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"clientId": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
"requestedServices": {
"Google Ads": {
"accessLevel": "ADMIN",
"requestedAccountLinks": [
{
"internalAccountId": "Xk7pQ2mR9vT4wB8nC3dF6hJ1"
}
]
},
"Meta Ads": {
"accessLevel": "['ADVERTISE', 'ANALYZE']",
"optional": true,
"requestedAccountLinks": [
{
"internalAccountId": "Lm5sZ8aE2gH6jK9qN3rU7yW4"
}
]
}
},
"intakeForm": {
"requested": true
},
"redirectUrl": "https://portal.example.com/onboarding/done"
}),
})
const { data } = await res.json() {
"data": {
"id": "2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
"clientId": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
"externalClientId": "crm-4821",
"status": "pending",
"stateVersion": 1,
"source": "api",
"inviteUrl": "https://acme.agencyaccess.co/i/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
"embedUrl": "https://acme.agencyaccess.co/i/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72?mode=embed",
"intakeForm": {
"requested": true,
"complete": false
},
"services": [
{
"service": "Google Ads",
"platform": "Google",
"accessLevel": "ADMIN",
"optional": false,
"status": "pending",
"accounts": [
{
"internalAccountId": "Xk7pQ2mR9vT4wB8nC3dF6hJ1",
"googleAdsMccAccountId": null,
"metaBusinessManagerId": null
}
],
"grantedAssets": []
},
{
"service": "Meta Ads",
"platform": "Meta",
"accessLevel": "['ADVERTISE', 'ANALYZE']",
"optional": true,
"status": "pending",
"accounts": [
{
"internalAccountId": "Lm5sZ8aE2gH6jK9qN3rU7yW4",
"googleAdsMccAccountId": null,
"metaBusinessManagerId": null
}
],
"grantedAssets": []
}
],
"thankYouMessage": null,
"redirectUrl": "https://portal.example.com/onboarding/done",
"createdAt": "2026-10-07T09:15:02.000Z",
"completedAt": null,
"expiresAt": "2026-10-21T09:15:02.000Z",
"cancelledAt": null
},
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0"
}
} List requests
GET/requests Lists requests, oldest first. Demo requests are never returned.
Query parameters
-
limitintegerResults per page, 1-100.
-
cursorstringThe
meta.nextCursorof the previous page. Cursors only work with the filters they were issued for. -
clientIduuidOnly this client's requests.
-
externalClientIdstringOnly requests of the client with this external ID.
-
statusstringOnly requests with this status.
One of
pending,in_progress,completed,cancelled,expired.
Returns
200 A page of requests.
Fields of each item (object)
-
iduuid -
clientIduuid -
externalClientIdstring | null -
statusstringpending: nothing granted, skipped or submitted yet (opening the link or signing in does not change it).in_progress: at least one service is granted or skipped, an invite awaits your confirmation, or the intake form is submitted.completed: every service is granted (optional ones may be skipped instead), not every service was skipped, and the intake form, if requested, is submitted. A completed request returns toin_progressif you ask for more access.cancelledandexpiredare final. One ofpending,in_progress,completed,cancelled,expired. -
stateVersionintegerIncreases with every change; use it to ignore older states and events.
-
sourcestringWhere the request was created. One of
api,dashboard,static_link. -
inviteUrlstringThe link the client opens to grant access.
-
embedUrlstringThe same link for an iframe in your portal.
-
intakeFormobjectShow fields of intakeForm
-
requestedboolean -
completeboolean
-
-
servicesarray of objectShow fields of services
-
servicestringThe service ID, such as
Google Ads. -
platformstringThe platform, such as
Google. -
accessLevelstring | nullThe requested role.
-
optionalbooleanThe client may skip it. Optional services still need to be granted or skipped for the request to complete.
-
statusstringOnly optional services can be skipped. One of
pending,granted,skipped. -
accountsarray of objectYour accounts access is requested for.
Show fields of accounts
-
internalAccountIdstring -
googleAdsMccAccountIdstring | null -
metaBusinessManagerIdstring | null
-
-
grantedAssetsarray of objectThe client's assets access was granted to.
Show fields of grantedAssets
-
idstringThe asset's ID on the platform.
-
namestring | null -
accessLevelstring | null
-
-
-
thankYouMessagestring | null -
redirectUrlstring | nullWhere the client is sent after finishing.
-
createdAtdatetime -
completedAtdatetime | null -
expiresAtdatetime | nullAfter this, no new grants; the request reports
expiredunless it is already completed or cancelled. Null when the request never expires. -
cancelledAtdatetime | null
- 400 The request is invalid (
VALIDATION_FAILED);detailslists the problems, withfieldwhen one applies. - 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl "https://api.agencyaccess.co/api/v2/requests?externalClientId=crm-4821" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" const res = await fetch("https://api.agencyaccess.co/api/v2/requests?externalClientId=crm-4821", {
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
},
})
const { data, meta } = await res.json()
// meta.nextCursor: pass as ?cursor= for the next page {
"data": [
{
"id": "2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
"clientId": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
"externalClientId": "crm-4821",
"status": "in_progress",
"stateVersion": 4,
"source": "api",
"inviteUrl": "https://acme.agencyaccess.co/i/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
"embedUrl": "https://acme.agencyaccess.co/i/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72?mode=embed",
"intakeForm": {
"requested": true,
"complete": false
},
"services": [
{
"service": "Google Ads",
"platform": "Google",
"accessLevel": "ADMIN",
"optional": false,
"status": "granted",
"accounts": [
{
"internalAccountId": "Xk7pQ2mR9vT4wB8nC3dF6hJ1",
"googleAdsMccAccountId": null,
"metaBusinessManagerId": null
}
],
"grantedAssets": [
{
"id": "1234567890",
"name": "1234567890",
"accessLevel": "ADMIN"
}
]
},
{
"service": "Meta Ads",
"platform": "Meta",
"accessLevel": "['ADVERTISE', 'ANALYZE']",
"optional": true,
"status": "pending",
"accounts": [
{
"internalAccountId": "Lm5sZ8aE2gH6jK9qN3rU7yW4",
"googleAdsMccAccountId": null,
"metaBusinessManagerId": null
}
],
"grantedAssets": []
}
],
"thankYouMessage": null,
"redirectUrl": "https://portal.example.com/onboarding/done",
"createdAt": "2026-10-07T09:15:02.000Z",
"completedAt": null,
"expiresAt": "2026-10-21T09:15:02.000Z",
"cancelledAt": null
}
],
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0",
"nextCursor": null
}
} Retrieve a request
GET/requests/{id} Returns the request with the status of every requested service and the assets access was granted to. Compare stateVersion to tell newer states from older ones.
Path parameters
-
iduuid requiredThe request ID.
Returns
200 The request.
Fields (object)
-
iduuid -
clientIduuid -
externalClientIdstring | null -
statusstringpending: nothing granted, skipped or submitted yet (opening the link or signing in does not change it).in_progress: at least one service is granted or skipped, an invite awaits your confirmation, or the intake form is submitted.completed: every service is granted (optional ones may be skipped instead), not every service was skipped, and the intake form, if requested, is submitted. A completed request returns toin_progressif you ask for more access.cancelledandexpiredare final. One ofpending,in_progress,completed,cancelled,expired. -
stateVersionintegerIncreases with every change; use it to ignore older states and events.
-
sourcestringWhere the request was created. One of
api,dashboard,static_link. -
inviteUrlstringThe link the client opens to grant access.
-
embedUrlstringThe same link for an iframe in your portal.
-
intakeFormobjectShow fields of intakeForm
-
requestedboolean -
completeboolean
-
-
servicesarray of objectShow fields of services
-
servicestringThe service ID, such as
Google Ads. -
platformstringThe platform, such as
Google. -
accessLevelstring | nullThe requested role.
-
optionalbooleanThe client may skip it. Optional services still need to be granted or skipped for the request to complete.
-
statusstringOnly optional services can be skipped. One of
pending,granted,skipped. -
accountsarray of objectYour accounts access is requested for.
Show fields of accounts
-
internalAccountIdstring -
googleAdsMccAccountIdstring | null -
metaBusinessManagerIdstring | null
-
-
grantedAssetsarray of objectThe client's assets access was granted to.
Show fields of grantedAssets
-
idstringThe asset's ID on the platform.
-
namestring | null -
accessLevelstring | null
-
-
-
thankYouMessagestring | null -
redirectUrlstring | nullWhere the client is sent after finishing.
-
createdAtdatetime -
completedAtdatetime | null -
expiresAtdatetime | nullAfter this, no new grants; the request reports
expiredunless it is already completed or cancelled. Null when the request never expires. -
cancelledAtdatetime | null
- 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 404 Not found, or it belongs to another agency.
- 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl "https://api.agencyaccess.co/api/v2/requests/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" const res = await fetch("https://api.agencyaccess.co/api/v2/requests/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72", {
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
},
})
const { data } = await res.json() {
"data": {
"id": "2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
"clientId": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
"externalClientId": "crm-4821",
"status": "in_progress",
"stateVersion": 4,
"source": "api",
"inviteUrl": "https://acme.agencyaccess.co/i/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
"embedUrl": "https://acme.agencyaccess.co/i/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72?mode=embed",
"intakeForm": {
"requested": true,
"complete": false
},
"services": [
{
"service": "Google Ads",
"platform": "Google",
"accessLevel": "ADMIN",
"optional": false,
"status": "granted",
"accounts": [
{
"internalAccountId": "Xk7pQ2mR9vT4wB8nC3dF6hJ1",
"googleAdsMccAccountId": null,
"metaBusinessManagerId": null
}
],
"grantedAssets": [
{
"id": "1234567890",
"name": "1234567890",
"accessLevel": "ADMIN"
}
]
},
{
"service": "Meta Ads",
"platform": "Meta",
"accessLevel": "['ADVERTISE', 'ANALYZE']",
"optional": true,
"status": "pending",
"accounts": [
{
"internalAccountId": "Lm5sZ8aE2gH6jK9qN3rU7yW4",
"googleAdsMccAccountId": null,
"metaBusinessManagerId": null
}
],
"grantedAssets": []
}
],
"thankYouMessage": null,
"redirectUrl": "https://portal.example.com/onboarding/done",
"createdAt": "2026-10-07T09:15:02.000Z",
"completedAt": null,
"expiresAt": "2026-10-21T09:15:02.000Z",
"cancelledAt": null
},
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0"
}
} Cancel a request
POST/requests/{id}/cancel Stops a request: the client can no longer sign in or grant access through it, and no more reminders can be sent. Sign-ins the client had not finished using are discarded. Access already granted is not revoked. Cancelling a cancelled request is fine; completed or expired requests return 409 INVALID_STATE.
Path parameters
-
iduuid requiredThe request ID.
Returns
200 The cancelled request.
Fields (object)
-
iduuid -
clientIduuid -
externalClientIdstring | null -
statusstringpending: nothing granted, skipped or submitted yet (opening the link or signing in does not change it).in_progress: at least one service is granted or skipped, an invite awaits your confirmation, or the intake form is submitted.completed: every service is granted (optional ones may be skipped instead), not every service was skipped, and the intake form, if requested, is submitted. A completed request returns toin_progressif you ask for more access.cancelledandexpiredare final. One ofpending,in_progress,completed,cancelled,expired. -
stateVersionintegerIncreases with every change; use it to ignore older states and events.
-
sourcestringWhere the request was created. One of
api,dashboard,static_link. -
inviteUrlstringThe link the client opens to grant access.
-
embedUrlstringThe same link for an iframe in your portal.
-
intakeFormobjectShow fields of intakeForm
-
requestedboolean -
completeboolean
-
-
servicesarray of objectShow fields of services
-
servicestringThe service ID, such as
Google Ads. -
platformstringThe platform, such as
Google. -
accessLevelstring | nullThe requested role.
-
optionalbooleanThe client may skip it. Optional services still need to be granted or skipped for the request to complete.
-
statusstringOnly optional services can be skipped. One of
pending,granted,skipped. -
accountsarray of objectYour accounts access is requested for.
Show fields of accounts
-
internalAccountIdstring -
googleAdsMccAccountIdstring | null -
metaBusinessManagerIdstring | null
-
-
grantedAssetsarray of objectThe client's assets access was granted to.
Show fields of grantedAssets
-
idstringThe asset's ID on the platform.
-
namestring | null -
accessLevelstring | null
-
-
-
thankYouMessagestring | null -
redirectUrlstring | nullWhere the client is sent after finishing.
-
createdAtdatetime -
completedAtdatetime | null -
expiresAtdatetime | nullAfter this, no new grants; the request reports
expiredunless it is already completed or cancelled. Null when the request never expires. -
cancelledAtdatetime | null
- 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 404 Not found, or it belongs to another agency.
- 409 A conflict: an
externalClientIdin use or immutable, an Idempotency-Key reused with a different body or still in progress, or a request in a state that does not allow this. - 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl -X POST "https://api.agencyaccess.co/api/v2/requests/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72/cancel" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" const res = await fetch("https://api.agencyaccess.co/api/v2/requests/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72/cancel", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
},
})
const { data } = await res.json() {
"data": {
"id": "2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
"clientId": "7c1e9f52-3b8a-4d2e-9a61-5f0c2d8b4e17",
"externalClientId": "crm-4821",
"status": "cancelled",
"stateVersion": 5,
"source": "api",
"inviteUrl": "https://acme.agencyaccess.co/i/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72",
"embedUrl": "https://acme.agencyaccess.co/i/2f6d8a14-91c3-4b7e-a5d0-8e3f1c6b9a72?mode=embed",
"intakeForm": {
"requested": true,
"complete": false
},
"services": [
{
"service": "Google Ads",
"platform": "Google",
"accessLevel": "ADMIN",
"optional": false,
"status": "granted",
"accounts": [
{
"internalAccountId": "Xk7pQ2mR9vT4wB8nC3dF6hJ1",
"googleAdsMccAccountId": null,
"metaBusinessManagerId": null
}
],
"grantedAssets": [
{
"id": "1234567890",
"name": "1234567890",
"accessLevel": "ADMIN"
}
]
},
{
"service": "Meta Ads",
"platform": "Meta",
"accessLevel": "['ADVERTISE', 'ANALYZE']",
"optional": true,
"status": "pending",
"accounts": [
{
"internalAccountId": "Lm5sZ8aE2gH6jK9qN3rU7yW4",
"googleAdsMccAccountId": null,
"metaBusinessManagerId": null
}
],
"grantedAssets": []
}
],
"thankYouMessage": null,
"redirectUrl": "https://portal.example.com/onboarding/done",
"createdAt": "2026-10-07T09:15:02.000Z",
"completedAt": null,
"expiresAt": "2026-10-21T09:15:02.000Z",
"cancelledAt": "2026-10-08T14:30:00.000Z"
},
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0"
}
}