# Calling Connector-Native Endpoints with the Proxy API

When a unified resource doesn't expose the field, endpoint, or verb a customer needs, the Apideck [Proxy API](/apis/proxy/reference) forwards an authenticated request to the underlying connector. Vault still handles OAuth, token refresh, and credential storage. The application keeps a single integration surface even when a specific call has to drop down to the connector's native API.

## Why a Unified API

The Unified APIs cover the resources that almost every customer asks for: accounts, contacts, invoices, employees, candidates. The Proxy API is the escape hatch for everything else, without giving up the parts of Vault that are tedious to rebuild.

- No connector-specific OAuth flows, token refresh, or revocation handling
- One consumer model and one set of Apideck headers, regardless of downstream
- Re-use the same connection a customer already authorized for the Unified API
- Reach native endpoints (custom objects, vendor-specific reports, beta APIs) without writing per-connector HTTP clients

## Resource mapping

The Proxy API exposes a single endpoint at `/proxy`. There is no `/proxy/{path}` template. The downstream URL is supplied entirely via the `x-apideck-downstream-url` header, and the HTTP verb on the proxy call selects the operation:

| HTTP verb on `/proxy` | Operation |
| --- | --- |
| `GET` | [`getProxy`](/apis/proxy/reference#operation/getProxy) |
| `POST` | [`postProxy`](/apis/proxy/reference#operation/postProxy) |
| `PUT` | [`putProxy`](/apis/proxy/reference#operation/putProxy) |
| `PATCH` | [`patchProxy`](/apis/proxy/reference#operation/patchProxy) |
| `DELETE` | [`deleteProxy`](/apis/proxy/reference#operation/deleteProxy) |
| `OPTIONS` | [`optionsProxy`](/apis/proxy/reference#operation/optionsProxy) |

By default the verb on the proxy request is also the verb forwarded downstream. Set `x-apideck-downstream-method` to override it (useful when a connector uses uncommon verbs or when the calling environment restricts `PATCH`).

## Walkthrough

### Read a native QuickBooks field

The unified `Invoice` model does not surface every QuickBooks `CustomField`. To read them, forward a `GET` to QuickBooks's native query endpoint. Vault injects the connector's authorization header so the calling code does not handle the OAuth bearer token directly.

```http
GET https://unify.apideck.com/proxy
Authorization: Bearer sk_live_8mP2NqRf7tH...
x-apideck-app-id: dSDmN8xK14ZpVcr3WJqL
x-apideck-consumer-id: user_7421
x-apideck-service-id: quickbooks
x-apideck-downstream-url: https://quickbooks.api.intuit.com/v3/company/9341452148237894/query?query=select%20*%20from%20Invoice%20where%20Id%20%3D%20'137'
```

Send this to [`GET /proxy`](/apis/proxy/reference#operation/getProxy). The response body is whatever QuickBooks returned, unwrapped. There is no Apideck envelope and no field renaming.

### Create a HubSpot custom object record

Custom objects are not part of the unified CRM model. Forward a `POST` to HubSpot's CRM v3 API with the native payload shape. Headers shown only on the first call are still required on every subsequent call.

```http
POST https://unify.apideck.com/proxy
x-apideck-service-id: hubspot
x-apideck-downstream-url: https://api.hubapi.com/crm/v3/objects/p_subscription
Content-Type: application/json
```

```json
{
  "properties": {
    "subscription_name": "Pro Annual",
    "mrr": 249.0,
    "renewal_date": "2025-11-30",
    "owner_email": "alex.rivera@northwind.io"
  }
}
```

Send this to [`POST /proxy`](/apis/proxy/reference#operation/postProxy). The body is forwarded verbatim, so it must match what HubSpot expects, not the unified shape.

### Use placeholder interpolation for tenant-specific hosts

Some connectors have a per-tenant base URL that is only known after the customer connects. Vault stores those values on the connection and exposes them as placeholders in the downstream URL. Apideck substitutes the placeholder before the request leaves the proxy.

For Microsoft Dynamics 365 Sales, the organisation URL is connection-specific. Reference it with `{organisation_url}`:

```http
GET https://unify.apideck.com/proxy
x-apideck-service-id: microsoft-dynamics-365-sales
x-apideck-downstream-url: {organisation_url}/api/data/v9.2/accounts?$select=name,revenue&$top=50
```

Send this to [`GET /proxy`](/apis/proxy/reference#operation/getProxy). The placeholder name has to match a setting that the connection actually stores. Inspect the connection in Vault to confirm which placeholders are available before referencing one in a downstream URL.

### Override the forwarded method or authorization

`x-apideck-downstream-method` is useful when proxying through a client that cannot send the verb directly, or when a downstream endpoint expects an unusual one:

```http
POST https://unify.apideck.com/proxy
x-apideck-service-id: salesforce
x-apideck-downstream-method: PATCH
x-apideck-downstream-url: https://example.my.salesforce.com/services/data/v60.0/sobjects/Account/0017Q00000pK3rJQAS
Content-Type: application/json
```

```json
{
  "Industry": "Renewable Energy",
  "AnnualRevenue": 18500000
}
```

Send this to [`POST /proxy`](/apis/proxy/reference#operation/postProxy). The proxy verb is `POST` (so the operation is `postProxy`) but the request reaches Salesforce as a `PATCH`.

`x-apideck-downstream-authorization` replaces the auth header Vault would inject. Reach for it only when the downstream call genuinely needs a different credential than the one Vault holds, for example a static API key for a sandbox or a service account token. Leave it unset for the default behavior, which is the right answer in almost every case.

```http
GET https://unify.apideck.com/proxy
x-apideck-service-id: greenhouse
x-apideck-downstream-url: https://harvest.greenhouse.io/v1/candidates?per_page=100
x-apideck-downstream-authorization: Basic YTNkZjkxYWVkOWMzNDI4ZjpfX18=
```

Send this to [`GET /proxy`](/apis/proxy/reference#operation/getProxy).

## Trade-offs

The Proxy API trades unification for reach. Plan for the following before designing around it:

- **No unified data shape.** The response is the connector's native body. Field names, casing, and nesting all change per connector.
- **Per-connector pagination.** Cursor, page, and offset semantics differ across QuickBooks, Salesforce, NetSuite, and so on. Pagination logic lives in the calling code.
- **No rate-limit normalization.** Status codes and `Retry-After` semantics pass through as the downstream returned them. Centralized retry logic from the Unified API does not apply.
- **No webhook normalization.** Anything created or changed via the proxy emits whatever events the connector natively emits, not the unified Apideck webhook events.
- **Downstream errors are forwarded.** A `400` from QuickBooks comes back as `400` with QuickBooks's error body. Distinguish proxy-level failures (auth, missing `x-apideck-downstream-url`) by checking for an Apideck error envelope before parsing native errors.

Use the proxy when the unified model is missing exactly what is needed. Stay on the Unified API for everything it already covers, especially anything that maps to a normalized webhook.

## Connector-specific behavior

Every connection authorized in Vault can be reached through the proxy. The notes below highlight base hosts and per-tenant cases worth knowing up front. For connectors with a single public host, point `x-apideck-downstream-url` at that host directly. For connectors with a per-tenant or per-region host, inspect the connection settings in Vault to find the placeholder name to interpolate.

| Connector | Notes |
| --- | --- |
| QuickBooks | Base host `https://quickbooks.api.intuit.com`. The realm ID is part of the URL path. |
| Xero | Base host `https://api.xero.com`. Multi-tenant: confirm whether the connection requires a tenant header for the call being made. |
| NetSuite | Account-specific host. Resolve the host from the connection's stored settings before constructing the URL. |
| Sage Intacct | Standard mapping. No known quirks beyond what the connector's native API documents. |
| Exact Online | Region-specific base host as stored on the connection. |
| FreshBooks | Account ID is part of the path on most endpoints. |
| Zoho Books | Region-specific host (`.com`, `.eu`, `.in` and others) per connection. |
| Dynamics 365 Business Central | Tenant-specific host. Use the `{organisation_url}` placeholder. |
| MYOB | Standard mapping. No known quirks beyond what the connector's native API documents. |
| Acumatica | Tenant-specific host. Resolve the host from the connection's stored settings. |
| Sage Business Cloud | Standard mapping. No known quirks beyond what the connector's native API documents. |
| BambooHR | Customer subdomain is part of the path. Resolve it from the connection's stored settings. |
| Personio | Base host `https://api.personio.de`. |
| HiBob | Base host `https://api.hibob.com`. |
| Workday | Tenant-specific host. The tenant and host are stored on the connection. |
| ADP Workforce Now | Standard mapping. No known quirks beyond what the connector's native API documents. |
| Rippling | Standard mapping. No known quirks beyond what the connector's native API documents. |
| Deel | Standard mapping. No known quirks beyond what the connector's native API documents. |
| Sage HR | Standard mapping. No known quirks beyond what the connector's native API documents. |
| Greenhouse | Harvest API at `https://harvest.greenhouse.io`. |
| Lever | Standard mapping. No known quirks beyond what the connector's native API documents. |
| Workable | Account slug is part of the path. Resolve it from the connection's stored settings. |
| Teamtailor | Standard mapping. No known quirks beyond what the connector's native API documents. |
| JazzHR | Standard mapping. No known quirks beyond what the connector's native API documents. |
| SmartRecruiters | Standard mapping. No known quirks beyond what the connector's native API documents. |
| Salesforce | Instance-specific host. Resolve the host from the connection's stored settings, and pin the API version in the path. |
| HubSpot | Base host `https://api.hubapi.com`. |
| Pipedrive | Company-specific host. Domain is stored on the connection. |
| Zoho CRM | Region-specific host (`.com`, `.eu`, `.in` and others) per connection. |
| Microsoft Dynamics 365 Sales | Tenant-specific host. Use the `{organisation_url}` placeholder. |
| Copper | Standard mapping. No known quirks beyond what the connector's native API documents. |

### Microsoft Dynamics 365 Sales and Business Central

Both connectors expose a per-tenant URL stored on the connection as `organisation_url`. Reference it with the placeholder rather than hard-coding the host, otherwise the request will fail for any consumer on a different tenant.

### Salesforce

Salesforce connections store the instance URL (for example `https://acme-corp.my.salesforce.com`) and the API version. Look up the placeholder name on the connection before composing the downstream URL, and pin the API version in the path so behavior does not silently change when Salesforce promotes a new default.

### NetSuite

NetSuite hosts are account-specific. The account identifier needs to be read from the connection settings rather than assumed. Some accounts also require a `Prefer: transient` header for SuiteQL queries, which can be set as a normal request header on the proxy call.

### BambooHR and Workable

Both rely on a customer-chosen slug in the URL. Resolve the slug from the connection's stored settings rather than the customer's company name, since the slug can differ from the display name.

## Next steps

- [Proxy API reference](/apis/proxy/reference)
- [Vault overview](/apis/vault/reference) for how connection settings (and placeholder values) are stored
- [Mark invoices as paid](/guides/mark-invoices-as-paid) for an example of staying inside the Unified API instead of dropping to native endpoints
