Add an outbound channel
Updated
Declare where a script may talk, as metadata in your model. The script
names the channel. The host owns the URL, the credential, the signer, and SSRF.
HttpClient is outside the script reference set.
Note
This page is the channel file. Enqueue versus send, posting, and polling are other doors — Send an outbound call, Enqueue an outbound call when a document posts, Poll a REST API from a job. The longer contract is Outbound integrations.
The JSON below is labelled. Channel wms-ship and dictionary WmsConnection
are an example, not a product object. Working country patterns you may copy
the shape of: fatoora-clearance (signer: fatoora,
connectionTable: TaxAuthorityConnection) and erpn-send (authMode: Login,
same connection dictionary). Do not treat a customization pack as the default.
How to#
1. The file — OutboundChannels/<name>.channel.json in your model,
kind OutboundChannel. New GUIDs on metaId. modelId is this model's
metaId. name is what the script will pass to EnqueueAsync / SendAsync.
{
"kind": "OutboundChannel",
"object": {
"path": "/shipments",
"method": "POST",
"signer": "none",
"authMode": "Header",
"authHeader": "Authorization",
"baseUrl": "https://wms.invalid/api",
"contentType": "application/json",
"connectionTable": "WmsConnection",
"devComments": "Example only — not a product channel. Live address comes from the tenant connection.",
"metaId": "<new guid>",
"name": "wms-ship",
"modelId": "<YOUR model guid>"
}
}
Typical object fields: path, method, signer, authMode, baseUrl,
contentType, connectionTable, metaId, name, modelId. Optional:
authHeader (Header mode), externalReferenceField (a top-level JSON
property of a 2xx body), devComments. GET / HEAD go with no body.
connectionTable names the tenant connection dictionary. The columns the
resolver reads are fixed: Code, BaseUrl, CredentialRef, Environment,
TimeoutSeconds, MaxRetries, AgentTag. Real dictionaries with that
shape: TaxAuthorityConnection (tax), UaBankConnection (bank). Empty
connectionTable falls back to TaxAuthorityConnection. That default is for
tax channels, not an invitation to put a warehouse token on a tax card.
2. The live address is the connection, not the file. baseUrl in the
channel is a default and is often a non-working placeholder
(https://wms.invalid/api, https://operator.invalid/erpn). Each tenant's
operator has its own host; some sit on the customer's LAN. The script passes
ConnectionRef = connection.Code. The resolver substitutes the rest.
Secrets never go in the workspace. CredentialRef is a name. The host
reads Integration:Credentials:<name>. Do not put a password, a Bearer token,
or a Basic blob in the JSON.
AgentTag lives on the connection, not in .channel.json. A neighbour
tenant may use a cloud operator; a loopback from Core is still SSRF. The agent
with that tag dials from its own machine.
3. A query that belongs to one document does not fit in path. Put a
relative path in header X-ZuloOne-RelativePath. The host strips it,
appends it to the connection BaseUrl, and does not put the header on the
wire. An absolute URL there is refused: the host still comes only from the
connection card.
authMode: Login (M.E.Doc REST): the host posts login
(POST /api/System/v2/login with {userLogin, password}) and sets
X-API-Key. The script never sees the password. Without a password the login
is skipped (stands without a live operator). Do not store a ready Bearer token
as the credential — that slot is the password.
4. Bump your modelVersion in the same change (minor: a new channel). The
tenant compares MetaPackages.PackageVersion to that number; an equal version
skips your model as a whole. Then POST /api/services/rebuild. Probe
without an outbox row: Developer → Outbound channels → Test channel, or
POST /api/metadata/outbound-channels/{id}/test —
Bump the model version.
Warning
Never
HttpClient/System.Net.Httpfrom a script. Name the channel. ConfigurationIntegration:Channels:<name>overrides the file on a stand (stub redirect). Do not ship a business channel inappsettings.json.A named
ConnectionRefthat is missing fromconnectionTableis an error, not a quiet fallback to the file'sbaseUrl. Falling back would send the document to a different host with nothing in the result to say so.
What's not there yet#
The platform does not accept an inbound callback that completes the send.
CompletionHandler is reserved and unread. Drain your own outbox for send
outcomes; poll the other side when you need a receipt —
Poll a REST API from a job. A document you
must not lose after posting goes through the outbox, not this file alone —
Enqueue an outbound call when a document posts.