curl --request POST \
--url https://api.bachs.io/v1/accounts/{account_id}/account-links \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"type": "onboarding",
"refresh_url": "https://adastores.example/connect/refresh",
"return_url": "https://adastores.example/connect/return"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
type: 'onboarding',
refresh_url: 'https://adastores.example/connect/refresh',
return_url: 'https://adastores.example/connect/return'
})
};
fetch('https://api.bachs.io/v1/accounts/{account_id}/account-links', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.bachs.io/v1/accounts/{account_id}/account-links"
payload = {
"type": "onboarding",
"refresh_url": "https://adastores.example/connect/refresh",
"return_url": "https://adastores.example/connect/return"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.bachs.io/v1/accounts/{account_id}/account-links"
payload := strings.NewReader("{\n \"type\": \"onboarding\",\n \"refresh_url\": \"https://adastores.example/connect/refresh\",\n \"return_url\": \"https://adastores.example/connect/return\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"id": "alnk_3b7e12c9d4a05f68b1c2",
"object": "connected_account_link",
"account": "acct_3Wq8ZfT1yHnJ5sVe",
"type": "onboarding",
"created": "2026-08-07T11:04:22.518Z",
"expires_at": "2026-09-06T11:04:22.518Z",
"url": "https://connect.bachs.io/setup/c/acct_3Wq8ZfT1yHnJ5sVe/al_kQ2v8nS1xJd0pR7mLtY4wZ6aHb3cFg9e",
"previous_link_superseded": false
}{
"detail": "Invalid request parameters",
"error_code": "VALIDATION_ERROR",
"errors": [
{
"field": "amount",
"message": "Amount must be a positive decimal string",
"type": "value_error"
}
]
}{
"detail": "Invalid API key",
"error_code": "UNAUTHORIZED"
}{
"detail": "API key does not have permission for this operation",
"error_code": "FORBIDDEN"
}{
"detail": "Resource not found",
"error_code": "NOT_FOUND"
}Create an account link
Issue a hosted link that walks an account through its outstanding requirements. Creating a link invalidates any outstanding active link of the same type for that account, so create one at the moment you redirect rather than on every page render. Requires an active connect capability on your own platform. See Onboarding.
curl --request POST \
--url https://api.bachs.io/v1/accounts/{account_id}/account-links \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"type": "onboarding",
"refresh_url": "https://adastores.example/connect/refresh",
"return_url": "https://adastores.example/connect/return"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
type: 'onboarding',
refresh_url: 'https://adastores.example/connect/refresh',
return_url: 'https://adastores.example/connect/return'
})
};
fetch('https://api.bachs.io/v1/accounts/{account_id}/account-links', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.bachs.io/v1/accounts/{account_id}/account-links"
payload = {
"type": "onboarding",
"refresh_url": "https://adastores.example/connect/refresh",
"return_url": "https://adastores.example/connect/return"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.bachs.io/v1/accounts/{account_id}/account-links"
payload := strings.NewReader("{\n \"type\": \"onboarding\",\n \"refresh_url\": \"https://adastores.example/connect/refresh\",\n \"return_url\": \"https://adastores.example/connect/return\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"id": "alnk_3b7e12c9d4a05f68b1c2",
"object": "connected_account_link",
"account": "acct_3Wq8ZfT1yHnJ5sVe",
"type": "onboarding",
"created": "2026-08-07T11:04:22.518Z",
"expires_at": "2026-09-06T11:04:22.518Z",
"url": "https://connect.bachs.io/setup/c/acct_3Wq8ZfT1yHnJ5sVe/al_kQ2v8nS1xJd0pR7mLtY4wZ6aHb3cFg9e",
"previous_link_superseded": false
}{
"detail": "Invalid request parameters",
"error_code": "VALIDATION_ERROR",
"errors": [
{
"field": "amount",
"message": "Amount must be a positive decimal string",
"type": "value_error"
}
]
}{
"detail": "Invalid API key",
"error_code": "UNAUTHORIZED"
}{
"detail": "API key does not have permission for this operation",
"error_code": "FORBIDDEN"
}{
"detail": "Resource not found",
"error_code": "NOT_FOUND"
}Authorizations
Bearer token authentication. Pass your API key as Authorization: Bearer sk_.... See Authentication for keys, scopes, and sandbox vs production.
Path Parameters
Body
What the account holder is being sent to do. onboarding: collect everything the account still owes for the first time. update: revisit information already collected, which requires the account to have requirements already and otherwise fails with 400 CONNECTED_ACCOUNT_REQUIREMENTS_NOT_FOUND. liveness: the selfie video check alone, for an account whose liveness_owed is true; otherwise it fails with 400 CONNECTED_ACCOUNT_LIVENESS_NOT_NEEDED.
onboarding, update, liveness "onboarding"
Response
Account link created. url is returned only here and cannot be read back.
What the link was issued for, echoing the type you sent. onboarding: the account holder is walked through everything the account still owes, for the first time. update: the account holder revisits information already collected, which only works once the account has requirements and otherwise fails with 400 CONNECTED_ACCOUNT_REQUIREMENTS_NOT_FOUND. liveness: the selfie video check alone.
onboarding, update, liveness "onboarding"
Send the account holder here. The URL carries a single-use credential, so deliver it over a channel you trust and keep it out of logs and analytics. It is returned only on this response and cannot be read back.
"https://connect.bachs.io/setup/c/acct_3Wq8ZfT1yHnJ5sVe/al_kQ2v8nS1xJd0pR7mLtY4wZ6aHb3cFg9e"

