---
title: "External Services and ERP Connectors"
category: "api-and-integration"
canonical: "https://www.ai-go.app/en-US/docs/guide/external-connectors"
locale: "en-US"
last_updated: "2026-08-10"
source: "AI GO 開發者文件（https://www.ai-go.app）"
---

# 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:

```python
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

```python
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 `http` scope, which is high-risk — granting it requires the owner's password
- Every outbound call is audited