Integration · Object storage
A container of exports, read on a cadence.
SharedKey auth against a storage account and container. Read the newest blob since the last run and map it; or write the mapped result back as one timestamped blob.
- Method
- POST
- Connector kind
- 1
How it works
What actually happens on a sync run
Azure signs differently from the other two — SharedKey over a canonicalized request rather than SigV4 or an OAuth token — and answers its listing in its own XML dialect. Everything above that is identical: list the prefix, take the newest blob modified since the last successful run, parse it by its own name, map it. The account key lives in the connector record, encrypted, and is never accepted in a request body.
- 1
Sign with SharedKey
The canonicalized request is signed with the account key on every call. - 2
List and filter
The container listing is normalized to the same object shape S3 and GCS produce, then filtered against the watermark. - 3
Parse by name
The blob name’s extension chooses the parser — text or binary. - 4
Map and land
An upload under your retention setting, then the cascade.
What you get
Built for files that keep arriving
Auth
SharedKey, encrypted at rest
The account key is stored envelope-encrypted and read only by the signer. A GET of the connector returns a four-character hint, never the key.
HowsignAzureRequest() builds the canonicalized string per call. Rotation happens in place via /rotate-secret, which overwrites the old key with no recovery window by design.
Incremental
Newest blob since the last successful run
A quiet container costs nothing but a listing, and reports success with zero rows rather than an error.
HowThe watermark is last_synced_at, overridable with ?since=. It advances only when the run succeeded.
Sovereign clouds
A non-public Azure environment is one field
Set the endpoint suffix and the same connector signs against a sovereign or government cloud host.
Howconfig.endpoint_suffix changes the host the signer targets; the SSRF guard still resolves and checks it like any other outbound request.
Secrets
The credential never travels in a request body
You reference a connector by id. Keys, tokens and service-account JSON are encrypted at rest and read only by the code that makes the call.
HownormalizeSecretField() folds every provider spelling — private_key, token, secret_access_key, account_key, service_account_json — into one auth_value field, which is KEK-envelope-encrypted before the row is written. A GET masks it to a 4-character hint. If encryption fails the field is dropped rather than stored in plaintext.
Configuration
The connector record, field by field
| Key | Required | What it is |
|---|---|---|
| container | Required | Container name. bucket is accepted as an alias. |
| account | Required | Storage account name. Required, and checked at save time. |
| auth_valuesecret | Required | The account key. account_key is accepted as an alias and folded into this field before storage. |
| endpoint_suffix | Optional | Non-public cloud suffix, for a sovereign or government Azure environment. |
| prefix | Optional | Blob prefix scoping the read and the write. |
| format | Optional | Override extension sniffing. |
| url | Optional | Legacy presigned/SAS URL form. Read-only. |
In code
A container drop, mapped on the next tick.
Save the account and container once; pull it on demand with a bearer key, or let the scheduled cadence on the record do it.
- POST
/v1/connectorsSave the connector. The secret is encrypted before it reaches Postgres.session - POST
/v1/connectors/{id}/testMake a real call and report what was actually proven.session - POST
/v1/connectors/{id}/syncPull now. Accepts ?since=<iso> to override the watermark.bearer - POST
/v1/gatewayAny input in, this destination populated, a delivery report out.bearer - POST
/v1/connectors/{id}/rotate-secretReplace the credential in place; the old one becomes unrecoverable.session
- The blob’s own name decides the parser, so an
.xlsxexport needs no configuration at all. - Azure’s error bodies are XML; the provider’s message is surfaced verbatim because an
AuthenticationFailedis the actionable part. - Blobs are capped at 25 MiB and calls time out at 60 seconds.
{
"kind": "azure_blob",
"name": "Payroll drop",
"template_id": "payroll",
"config": {
"account": "acmepayroll",
"container": "exports",
"prefix": "monthly/",
"account_key": "…"
}
}curl -X POST https://api.adaptivmapr.com/v1/connectors/con_ab77…/sync \
-H "Authorization: Bearer $MAPR_API_KEY"{
"ok": true,
"status": "parsed",
"upload_id": "upl_5c02…",
"row_count": 412,
"accepted": 412,
"format": "xlsx",
"incremental": "incremental"
}Limits & failure modes
What it refuses, and what it tells you
| Code | When | What to do |
|---|---|---|
400 ssrf_blocked | The configured host resolves to a private, link-local or loopback address. | Every outbound request is DNS-resolved and checked before it is made, on the scheduled path and the on-demand path alike. The reason is returned with the code. |
400 config_invalid | A credential or identifier the provider needs is missing or malformed. | Refused at save time rather than at 3am during a scheduled run. The message names the exact key. |
400 config_invalid | No account, or no account key. | Both are checked before the record is created; a container with no way to sign for it is never saved. |
413 fetch_too_large | The chosen blob exceeds 25 MiB. | Split it upstream. The ceiling is the parser’s memory envelope, not an Azure limit. |
502 fetch_failed | Azure refused the list or the get. | Its own message is passed through and the watermark is left where it was. |
Incremental sync
Real, by blob modified time — the listing is filtered against the watermark and only the newest survivor is read. An empty result is status: "not_modified", a success.
PHI & residency
X-PHI and X-Region to phi-cloud so a regulated run lands on an in-region, BAA-eligible model, it costs +20% on the whole charge, and it is locked until the workspace accepts the BAA in Settings → Security & Data. An explicit PHI ask without an acceptance is 403 agreement_required, never a silent downgrade. A standard run keeps the workspace’s region pin — the region decides where compute may run, and the sandbox refuses a region-less run.What it costs
Billed on the same prepaid wallet
Moving bytes is not a line item. A sync that pulls a file and a destination write that lands the rows are both part of one map, and the map is what the wallet sees. There is no free tier, no seats and no contract — top up from $10, a balance shared across the phi-cloud suite.
| Charge | Rate | Notes |
|---|---|---|
| Every map | $0.001 | A flat per-map fee — a few tokens — charged even when the run was fully deterministic or hit the layout cache and used no AI at all. |
| AI, only when it ran | at cost × 2 | Layer-5 cleanup, any-to-any convert and structural reshape bill the phi-cloud tokens actually consumed. Bring your own model key and it is × 0.5. |
| PHI / enterprise routing | +20% | Multiplies the whole charge, flat fee included — and only when the run genuinely got that routing. Locked until the workspace accepts the BAA in-app. |
Questions
Before you wire it up
Does this support a SAS token instead of an account key?
Can I point it at a sovereign or government Azure cloud?
How often does a scheduled connector run?
What happens to the file after it is mapped?
Verified against lib/objectStore.ts · lib/cloudAuth.ts · lib/connectorSyncRunner.ts · app/api/v1/connectors/route.ts
Azure and Azure Blob Storage are trademarks of Microsoft Corporation. Named here to describe interoperability only — no affiliation, endorsement or partnership is claimed.
Point it at Azure Blob Storage. Get your schema back.
Start with a $10 prepaid wallet. Every map draws a few tokens; in schema-only mode only headers and up to three sample rows, clamped to 80 characters a cell, ever leave you.