Problem
Two related gaps in the published OpenAPI spec (GET /api/v2/openapi.json) that made it very hard to configure a gupshup instance correctly without reading the bundled source.
Gap 1 — Instance schema returns ~80 fields but spec lists only 14
components.schemas.Instance.properties declares:
agentId, agentProviderId, agentStreamMode, agentTimeout, channel,
createdAt, id, isActive, isDefault, name, ownerIdentifier,
profileName, profilePicUrl, updatedAt
But GET /api/v2/instances/{id} actually returns ~80 fields including:
gupshupCallbackUrl, gupshupEventId — required for gupshup channel to work
gupshupAuthToken — secret, masked in GET response but DOES exist in DB and IS accepted by PATCH (we confirmed by setting it via PATCH and observing the behavior change downstream)
twilioAccountSid, twilioFrom, twilioMessagingServiceSid, twilioStatusCallbackUrl, twilioWebhookUrl, twilioValidateSignature
agentReplyFilter, agentSessionStrategy, agentPrefixSenderName, agentGateEnabled/Model/Prompt, agentStalledTimeoutMs, agentChainToInstanceId, chainMode
triggerEvents, triggerReactions, triggerMentionPatterns, triggerMode, inboundMaxAgeMinutes
messageDebounce*, messageSplitDelay*, enableAutoSplit, messageFormatMode
accessMode, processAudio/Images/Video/Documents, agentWaitForMedia, agentSendMediaPath, agentSendMediaPathTypes
readReceipts, markOnlineOnConnect, reactionAck, reactionAckEmoji, ackTimeoutMs, agentAckMessage
ttsVoiceId, ttsModelId
replayEnabled, requireGenieSignature, bridgeTmuxSession, followUpConfig, sessionReset
- …and more
components.schemas.CreateInstanceRequest is even thinner — only 7 properties, plus token (which is described as "Bot token for Discord instances"). For non-Discord channels there's no way from the spec to know which channel-specific fields are accepted.
Gap 2 — channel-specific webhook receiver path not documented
The spec documents only the generic POST /webhooks/{source} (creates a "custom event"). But the channel-aware webhook delivery actually lives at:
POST /api/v2/channels/{source}/{instanceId}/webhook?token=<webhook_verify_token>
This is what gupshup is configured to POST to (and what the iPaaS calls back). It's also what the /webhook-sources/gupshup counter reflects: only 2 hits there because all real traffic goes through /channels/gupshup/{id}/webhook?token= instead.
Auth semantics for this path:
?token=... is checked against instances.webhook_verify_token; mismatch → 401
x-api-key is not required
- Empty
{} body returns 200 OK
- The channel plugin parses the body and dispatches per-instance
None of that is in the spec.
Why this matters
We spent multiple hours probing endpoints, reverse-engineering from the bundled dist/server/index.js, and asking around to figure out:
- What field holds the gupshup "Authorization" token for outbound (turns out:
gupshupAuthToken, undocumented + masked from GET)
- What event ID format the gupshup iPaaS expects (turns out: bare
nx_omni_agent_reply, not the <uuid>.nx_omni_agent_reply form some configs use)
- Where gupshup should POST inbound webhooks (turns out:
/channels/gupshup/{id}/webhook?token=, not the generic /webhooks/gupshup)
An accurate spec would have made each of these a 1-minute lookup.
Suggested fix
- Generate
Instance schema from the same source-of-truth as the GET serializer (column list + types). Mark gupshupAuthToken as format: password / writeOnly: true so it's visible-and-settable but masked in GET responses.
- Generate
UpdateInstanceRequest (PATCH) and CreateInstanceRequest (POST) with the full property set, marking channel-conditional fields with description notes (e.g. "Required when channel=gupshup").
- Add
POST /channels/{source}/{instanceId}/webhook with the ?token= query param to the spec, tagged Webhooks, with the auth semantics documented.
Environment
Problem
Two related gaps in the published OpenAPI spec (
GET /api/v2/openapi.json) that made it very hard to configure a gupshup instance correctly without reading the bundled source.Gap 1 —
Instanceschema returns ~80 fields but spec lists only 14components.schemas.Instance.propertiesdeclares:But
GET /api/v2/instances/{id}actually returns ~80 fields including:gupshupCallbackUrl,gupshupEventId— required for gupshup channel to workgupshupAuthToken— secret, masked in GET response but DOES exist in DB and IS accepted by PATCH (we confirmed by setting it via PATCH and observing the behavior change downstream)twilioAccountSid,twilioFrom,twilioMessagingServiceSid,twilioStatusCallbackUrl,twilioWebhookUrl,twilioValidateSignatureagentReplyFilter,agentSessionStrategy,agentPrefixSenderName,agentGateEnabled/Model/Prompt,agentStalledTimeoutMs,agentChainToInstanceId,chainModetriggerEvents,triggerReactions,triggerMentionPatterns,triggerMode,inboundMaxAgeMinutesmessageDebounce*,messageSplitDelay*,enableAutoSplit,messageFormatModeaccessMode,processAudio/Images/Video/Documents,agentWaitForMedia,agentSendMediaPath,agentSendMediaPathTypesreadReceipts,markOnlineOnConnect,reactionAck,reactionAckEmoji,ackTimeoutMs,agentAckMessagettsVoiceId,ttsModelIdreplayEnabled,requireGenieSignature,bridgeTmuxSession,followUpConfig,sessionResetcomponents.schemas.CreateInstanceRequestis even thinner — only 7 properties, plustoken(which is described as "Bot token for Discord instances"). For non-Discord channels there's no way from the spec to know which channel-specific fields are accepted.Gap 2 — channel-specific webhook receiver path not documented
The spec documents only the generic
POST /webhooks/{source}(creates a "custom event"). But the channel-aware webhook delivery actually lives at:This is what gupshup is configured to POST to (and what the iPaaS calls back). It's also what the
/webhook-sources/gupshupcounter reflects: only 2 hits there because all real traffic goes through/channels/gupshup/{id}/webhook?token=instead.Auth semantics for this path:
?token=...is checked againstinstances.webhook_verify_token; mismatch → 401x-api-keyis not required{}body returns200 OKNone of that is in the spec.
Why this matters
We spent multiple hours probing endpoints, reverse-engineering from the bundled
dist/server/index.js, and asking around to figure out:gupshupAuthToken, undocumented + masked from GET)nx_omni_agent_reply, not the<uuid>.nx_omni_agent_replyform some configs use)/channels/gupshup/{id}/webhook?token=, not the generic/webhooks/gupshup)An accurate spec would have made each of these a 1-minute lookup.
Suggested fix
Instanceschema from the same source-of-truth as the GET serializer (column list + types). MarkgupshupAuthTokenasformat: password/writeOnly: trueso it's visible-and-settable but masked in GET responses.UpdateInstanceRequest(PATCH) andCreateInstanceRequest(POST) with the full property set, marking channel-conditional fields withdescriptionnotes (e.g. "Required when channel=gupshup").POST /channels/{source}/{instanceId}/webhookwith the?token=query param to the spec, taggedWebhooks, with the auth semantics documented.Environment
2.260531.5