External Services and ERP Connectors
The previous chapter covered how others call AI GO. This one covers the opposite direction — how AI GO calls out.
There are two mechanisms with different purposes:
| Mechanism | Purpose | Configured in |
|---|---|---|
| External services (egress) | Custom Apps calling arbitrary external APIs | The Builder's External Services tab |
| External data connectors | Column-level synchronization with existing ERPs and databases | Dashboard → Data Center → External Data |
1. External services: an application's outbound channel
Custom Apps cannot reach the internet freely. Every outbound call must go through an authorized external service. That is deliberate — it prevents applications from sending enterprise data somewhere nobody approved.
Where to configure
The Builder's External Services tab is the only entry point. What you configure there is which external services this application may call.
Domain allowlist
Authorization is granted per domain: authorize api.logistics.example.com and the application can only reach paths under that domain. Unauthorized domains are unreachable regardless of what the code says.
Authentication: credentials belong to the application
The gateway does domain validation only — it does not hold credentials, and it neither injects nor strips the Authorization header. Credentials for external APIs live in the application's App Secrets (ctx.secrets), and the server action assembles its own authentication header at call time:
def execute(ctx):
resp = ctx.http.call('logistics', '/track',
method='POST',
headers={'Authorization': f"Bearer {ctx.secrets.get('LOGISTICS_KEY')}"},
body={'no': ctx.params['tracking_no']})
If the external API returns 401, the application's own credentials are at fault (a malformed header, an expired key) — check App Secrets and the code; no change to the external service configuration is needed.
Using it in code
def execute(ctx):
resp = ctx.http.call('logistics', '/track',
method='POST',
body={'no': ctx.params['tracking_no']})
if resp['status'] != 200:
# data carries error_type / error / fix
return ctx.response.json({'error': resp['data'].get('fix')})
ctx.response.json(resp['data'])
The first argument is the service identifier, not a full URL; the path is appended to that service's base URL.
Actionable error messages
When an outbound call fails, data carries three fields:
| Field | Contents |
|---|---|
error_type | Classification (unauthorized, timeout, bad credentials, domain not allowlisted…) |
error | Technical message |
fix | A single sentence you can relay to the user as a remedy |
fix exists by design — it lets an application show "how to fix this" to the operator instead of a technical stack trace. The Builder UI and the AI development assistant also read it when helping you debug.
Symptoms of a missing authorization
When code calls a service that has no matching external-service entry, or one not authorized for this application, the gateway blocks it at runtime and the response's fix field says what's missing. The platform neither validates nor stores credentials — a missing key only surfaces as a 401 when the external API is actually called, and is the application's error to handle.
Fetching arbitrary URLs
Besides named services, ctx.http.fetch can retrieve arbitrary public URLs (for example a public exchange rate or notice page). Security restrictions remain fully in force — it cannot be used to reach internal network addresses.
2. External data connectors: synchronizing with existing systems
Entry point: Dashboard → Data Center → External Data. Requires system.admin.
Unlike external services, this mechanism handles structured data synchronization — mapping an existing ERP or database to AI GO's data model and syncing on a schedule.
Supported sources
- Generic RESTful APIs
- Generic external databases (PostgreSQL, MySQL, and others)
- Preset connection profiles for mainstream ERP systems
After creating a data source you can test the connection to verify reachability and health.
Field mapping
Once connected, external columns must be mapped to AI GO's data model.
Mapping templates: common ERP systems ship preset templates (customer master, product master, and so on) that a wizard can apply.
AI mapping suggestions: for custom tables or non-standard systems, AI analyzes semantic similarity between external column names and types and AI GO's columns, proposing the best correspondence. This substantially reduces column-by-column configuration.
Direction and logs
| Direction | Notes |
|---|---|
| External → AI GO | Read-only pull |
| AI GO → external | Write back |
| Bidirectional | Both ways |
Sync logs record each run's timestamp, row counts, status, and error messages for tracking consistency and troubleshooting.
3. Which one to use
| Situation | Use |
|---|---|
| App calls an SMS API to send a code | External service |
| App queries logistics status | External service |
| App calls your own model API | External service + App Secrets |
| Sync an existing ERP's customer master in | External data connector |
| Keep order data consistent across two systems | External data connector |
| One-time migration of legacy data | Data import (Chapter 6) |
Rule of thumb: single calls use external services, ongoing sync uses connectors, one-time migration uses import.
4. The other direction: letting external systems push to you
If the external system supports event push, don't poll — use a webhook and let it call you.
Enabling webhooks on a Custom App action gives you a public endpoint:
POST https://{app subdomain}/webhook/{action_name}
Always verify the caller inside the action (for example by comparing a signature with ctx.crypto.hmac_sign). See Chapter 9.
5. Security boundary summary
- Applications can only call explicitly authorized domains
- Outbound credentials live in App Secrets, never in application source
- Internal network addresses are protected and cannot be reached by fetching arbitrary URLs
- Calling external services requires the
httpscope, which is high-risk — granting it requires the owner's password - Every outbound call is audited