integrations
https://api.example.com
/api/v1/brands/{brand_id}/integrations
List Integration Overview
Overview of all configured providers with aggregated stats for a brand.
brand_id
integer
path
required
200Successful Response422Validation Error
curl -X GET 'https://api.example.com/api/v1/brands/{brand_id}/integrations' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.get(
"https://api.example.com/api/v1/brands/{brand_id}/integrations",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations", {
method: "GET",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.example.com/api/v1/brands/{brand_id}/integrations", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
/api/v1/brands/{brand_id}/integrations
Create Integration
Create a new API integration key. Brand admin only.
brand_id
integer
path
required
200Successful Response422Validation Error
curl -X POST 'https://api.example.com/api/v1/brands/{brand_id}/integrations' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{
"provider_name": "string",
"key_label": "string",
"credentials": {},
"daily_limit": 0,
"minute_limit": 0,
"is_primary": false,
"country_code": "string",
"spend_limit_amount": 0.0,
"spend_limit_period": "string",
"spend_limit_action": "string",
"share_with_pool": false
}'
import httpx
resp = httpx.post(
"https://api.example.com/api/v1/brands/{brand_id}/integrations",
headers={"Authorization": "Bearer ", "Content-Type": "application/json"},
json={"provider_name": "string", "key_label": "string", "credentials": {}, "daily_limit": 0, "minute_limit": 0, "is_primary": false, "country_code": "string", "spend_limit_amount": 0.0, "spend_limit_period": "string", "spend_limit_action": "string", "share_with_pool": false},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations", {
method: "POST",
headers: { "Authorization": "Bearer ", "Content-Type": "application/json" },
body: JSON.stringify({"provider_name": "string", "key_label": "string", "credentials": {}, "daily_limit": 0, "minute_limit": 0, "is_primary": false, "country_code": "string", "spend_limit_amount": 0.0, "spend_limit_period": "string", "spend_limit_action": "string", "share_with_pool": false})
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"provider_name": "string", "key_label": "string", "credentials": {}, "daily_limit": 0, "minute_limit": 0, "is_primary": false, "country_code": "string", "spend_limit_amount": 0.0, "spend_limit_period": "string", "spend_limit_action": "string", "share_with_pool": false}`)
req, _ := http.NewRequest("POST", "https://api.example.com/api/v1/brands/{brand_id}/integrations", body)
req.Header.Set("Authorization", "Bearer ")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
Request body
{"key_label":"string","is_primary":false,"credentials":{},"daily_limit":0,"country_code":"string","minute_limit":0,"provider_name":"string","share_with_pool":false,"spend_limit_action":"string","spend_limit_amount":0,"spend_limit_period":"string"}
/api/v1/brands/{brand_id}/integrations/providers
List Provider Templates
List available provider templates with field schemas.
brand_id
integer
path
required
200Successful Response422Validation Error
curl -X GET 'https://api.example.com/api/v1/brands/{brand_id}/integrations/providers' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.get(
"https://api.example.com/api/v1/brands/{brand_id}/integrations/providers",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations/providers", {
method: "GET",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.example.com/api/v1/brands/{brand_id}/integrations/providers", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
/api/v1/brands/{brand_id}/integrations/all
List Integrations
List all integration keys for a brand, optionally filtered by provider.
brand_id
integer
path
required
provider_name
any
query
200Successful Response422Validation Error
curl -X GET 'https://api.example.com/api/v1/brands/{brand_id}/integrations/all' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.get(
"https://api.example.com/api/v1/brands/{brand_id}/integrations/all",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations/all", {
method: "GET",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.example.com/api/v1/brands/{brand_id}/integrations/all", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
/api/v1/brands/{brand_id}/integrations/{integration_id}
Get Integration
Get a single integration key detail.
brand_id
integer
path
required
integration_id
integer
path
required
200Successful Response422Validation Error
curl -X GET 'https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.get(
"https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}", {
method: "GET",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
/api/v1/brands/{brand_id}/integrations/{integration_id}
Update Integration
Update an integration key. Brand admin only.
brand_id
integer
path
required
integration_id
integer
path
required
200Successful Response422Validation Error
curl -X PATCH 'https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{
"key_label": "string",
"credentials": {},
"daily_limit": 0,
"minute_limit": 0,
"is_primary": true,
"is_active": true,
"country_code": "string",
"spend_limit_amount": 0.0,
"spend_limit_period": "string",
"spend_limit_action": "string",
"share_with_pool": true
}'
import httpx
resp = httpx.patch(
"https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}",
headers={"Authorization": "Bearer ", "Content-Type": "application/json"},
json={"key_label": "string", "credentials": {}, "daily_limit": 0, "minute_limit": 0, "is_primary": true, "is_active": true, "country_code": "string", "spend_limit_amount": 0.0, "spend_limit_period": "string", "spend_limit_action": "string", "share_with_pool": true},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}", {
method: "PATCH",
headers: { "Authorization": "Bearer ", "Content-Type": "application/json" },
body: JSON.stringify({"key_label": "string", "credentials": {}, "daily_limit": 0, "minute_limit": 0, "is_primary": true, "is_active": true, "country_code": "string", "spend_limit_amount": 0.0, "spend_limit_period": "string", "spend_limit_action": "string", "share_with_pool": true})
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"key_label": "string", "credentials": {}, "daily_limit": 0, "minute_limit": 0, "is_primary": true, "is_active": true, "country_code": "string", "spend_limit_amount": 0.0, "spend_limit_period": "string", "spend_limit_action": "string", "share_with_pool": true}`)
req, _ := http.NewRequest("PATCH", "https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}", body)
req.Header.Set("Authorization", "Bearer ")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
Request body
{"is_active":true,"key_label":"string","is_primary":true,"credentials":{},"daily_limit":0,"country_code":"string","minute_limit":0,"share_with_pool":true,"spend_limit_action":"string","spend_limit_amount":0,"spend_limit_period":"string"}
/api/v1/brands/{brand_id}/integrations/{integration_id}
Delete Integration
Delete an integration key. Brand admin only.
brand_id
integer
path
required
integration_id
integer
path
required
200Successful Response422Validation Error
curl -X DELETE 'https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.delete(
"https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}", {
method: "DELETE",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
/api/v1/brands/{brand_id}/integrations/{integration_id}/reset-usage
Reset Integration Usage
Reset daily and minute usage counters. Brand admin only.
brand_id
integer
path
required
integration_id
integer
path
required
200Successful Response422Validation Error
curl -X POST 'https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/reset-usage' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.post(
"https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/reset-usage",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/reset-usage", {
method: "POST",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("POST", "https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/reset-usage", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
/api/v1/brands/{brand_id}/integrations/spend/provider/{provider_name}
Get Provider Spend Overview
Get aggregated spend overview for a provider.
brand_id
integer
path
required
provider_name
string
path
required
200Successful Response422Validation Error
curl -X GET 'https://api.example.com/api/v1/brands/{brand_id}/integrations/spend/provider/{provider_name}' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.get(
"https://api.example.com/api/v1/brands/{brand_id}/integrations/spend/provider/{provider_name}",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations/spend/provider/{provider_name}", {
method: "GET",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.example.com/api/v1/brands/{brand_id}/integrations/spend/provider/{provider_name}", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
/api/v1/brands/{brand_id}/integrations/{integration_id}/spend
Get Integration Spend
Get spend data for a specific integration with history.
brand_id
integer
path
required
integration_id
integer
path
required
200Successful Response422Validation Error
curl -X GET 'https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/spend' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.get(
"https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/spend",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/spend", {
method: "GET",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/spend", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
/api/v1/brands/{brand_id}/integrations/{integration_id}/test
Test Integration Connection
Test if an API key is working by making a lightweight API call.
brand_id
integer
path
required
integration_id
integer
path
required
200Successful Response422Validation Error
curl -X POST 'https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/test' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.post(
"https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/test",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/test", {
method: "POST",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("POST", "https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/test", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
/api/v1/brands/{brand_id}/integrations/{integration_id}/sync-billing
Sync Integration Billing
Trigger billing sync for an integration using its billing adapter.
brand_id
integer
path
required
integration_id
integer
path
required
200Successful Response422Validation Error
curl -X POST 'https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/sync-billing' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.post(
"https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/sync-billing",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/sync-billing", {
method: "POST",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("POST", "https://api.example.com/api/v1/brands/{brand_id}/integrations/{integration_id}/sync-billing", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}
/api/v1/admin/integrations/rotate-keys
Rotate All Keys
Re-encrypt every active integration's ``credentials_encrypted`` with the newest Fernet key. Used by the key-rotation runbook (docs/services/fastapi/key-rotation.md, shipped in 3.2): after a new CREDENTIALS_ENCRYPTION_KEY is promoted and the old one moved to CREDENTIALS_ENCRYPTION_KEY_PREVIOUS, this drains every row off the retired key so the previous key can eventually be removed. SCOPE (3.2b): rotates the ``credentials_encrypted`` column ONLY. The three OAuth columns (``access_token_encrypted`` / ``refresh_token_encrypted`` / ``id_token_encrypted``) are NOT yet re-encrypted here — see the runbook's "Deferred follow-up". For VayaPin's current providers (google_maps / aftership / twilio, all ``api_key`` auth) those columns are NULL, so a single-column pass fully drains the old key. Do NOT retire CREDENTIALS_ENCRYPTION_KEY_PREVIOUS once any OAuth-auth integration exists until the OAuth columns are covered, or those tokens strand. - Super-admin only (``require_super_admin``). - Keyset pagination over ``id`` (index-friendly, stable under concurrent inserts), committing one batch of 100 per transaction to keep locks short. - Idempotent: rotating a token already on the newest key is a no-op re-encrypt. - Failure-isolated: a row whose token decrypts with no configured key is recorded in ``failed_ids`` and skipped — it never aborts the rest of the run. - No plaintext is logged or returned; ``rotate_credential`` stays ciphertext→ciphertext.
200Successful Response
curl -X POST 'https://api.example.com/api/v1/admin/integrations/rotate-keys' \
-H 'Authorization: Bearer '
import httpx
resp = httpx.post(
"https://api.example.com/api/v1/admin/integrations/rotate-keys",
headers={"Authorization": "Bearer "},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.example.com/api/v1/admin/integrations/rotate-keys", {
method: "POST",
headers: { "Authorization": "Bearer " }
});
const data = await resp.json();
console.log(data);
package main
import (
"net/http"
)
func main() {
req, _ := http.NewRequest("POST", "https://api.example.com/api/v1/admin/integrations/rotate-keys", nil)
req.Header.Set("Authorization", "Bearer ")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
}