[object Object]

The decision between RESTMessage, RESTMessageV2, and plain HTTP wrappers is not a religious question. It is a question about how much of the platform’s outbound machinery you want involved in this particular call, and the right answer differs by integration. Defaulting to V2 is correct 80 percent of the time and disastrous the other 20.

What V2 buys you and what it costs

RESTMessageV2 is the modern API. It gives you parameterized message records stored in the platform, centralized auth profile management, MID server routing, request and response logging through sys_rest_message_log, and full participation in the platform’s outbound request monitoring.

The cost: every call is a heavier object. You pay for the parameter resolution, the auth profile lookup, the logging machinery, and the integration with the platform’s monitoring framework. For low-volume calls — anything fewer than 1,000 per hour — the cost is invisible. For high-volume calls — synchronous integrations doing hundreds of calls per minute — the cost is real.

When V1 still fits

RESTMessage (V1) is older and less featureful, but it exists for a reason. It survives because some legacy integrations have endpoint configurations stored only in V1 records and migrating them carries no benefit. If you inherit a working V1 integration, do not migrate it for migration’s sake. Migrate when:

  • The endpoint or auth needs to change in ways V1 cannot express
  • You need MID server routing that V1 does not support cleanly
  • You need outbound logging through sys_rest_message_log for compliance

If none of these apply, V1 is fine. The “always use V2” advice you read online assumes you have a free week to rebuild working integrations. You probably do not.

When plain HTTP wins

This is the case most teams overlook. For a small number of integrations — high-volume, latency-sensitive, with their own auth and retry logic — plain sn_ws.RESTClient or even a low-level XMLHttpRequest wrapper is the right tool. The cases:

  • A high-volume webhook subscriber that fires hundreds of times per minute and needs every millisecond
  • An integration with a non-standard auth scheme that the V2 auth profile cannot represent
  • A streaming or long-poll endpoint that does not fit the request-response model V2 assumes
  • A test harness that needs to deliberately bypass platform logging

The discipline: justify in the script comments why you chose plain HTTP over V2. The next engineer who reads the code will assume “did not know about V2” unless you tell them otherwise.

A V2 example with the right hygiene

var r = new sn_ws.RESTMessageV2('ACME Billing', 'GetInvoice');
r.setStringParameterNoEscape('invoice_id', invoiceId);
r.setRequestHeader('X-Correlation-Id', gs.generateGUID());
r.setHttpTimeout(20000); // 20 seconds — never leave this at default

try {
    var resp = r.execute();
    var status = resp.getStatusCode();
    var body = resp.getBody();
    if (status < 200 || status >= 300) {
        gs.warn('ACME GetInvoice non-2xx: ' + status + ' body=' + body);
        return null;
    }
    return JSON.parse(body);
} catch (e) {
    gs.error('ACME GetInvoice exception: ' + e.message);
    return null;
} finally {
    // RESTMessageV2 cleans up after itself; nothing required here, but
    // remember to clear sensitive locals if any were captured.
}

Four things every V2 call should have:

  1. An explicit timeout. The default is too long and the call will hang transactions if the partner is slow.
  2. A correlation ID header so the partner’s logs and yours can be joined when something goes wrong.
  3. Status code checking with an explicit non-2xx path. execute() does not throw on HTTP errors; you must.
  4. A try/catch around the call. Network exceptions and platform exceptions both surface here.

The auth profile choice

V2 supports several auth profile types: Basic, OAuth 2.0, JWT, mutual TLS, AWS signature, and custom. The decision tree:

  • Partner offers OAuth 2.0 — use it. The token refresh is handled and the audit trail is cleaner than basic.
  • Partner offers only basic auth — wrap the credential in the platform’s vault, never in plain text in the message record.
  • Partner offers mutual TLS — use it where the platform supports it; otherwise consider MID server routing.
  • Partner uses a custom header-based scheme — write a custom auth profile or use a Script Include to generate the header and pass it with setRequestHeader.

Never put credentials in a script. Never put them in a system property. The vault is the only acceptable home.

For the related cross-cutting concern of credential rotation, see our zero-downtime credential rotation pattern.

Retry and idempotency

V2 does not retry on failure by default. You must decide:

  • For idempotent reads (GET with no side effects), retry with exponential backoff. Two retries is usually enough; three is the maximum before you should accept the failure.
  • For non-idempotent writes (POST that creates a record), do not retry unless the partner supports idempotency keys. A blind retry of a POST is how you create duplicate invoices.
  • For partner-supported idempotency keys, generate the key client-side and include it in the request. The partner will dedupe.
function withRetry(callFn, maxAttempts) {
    var attempt = 0;
    var backoff = 500;
    while (attempt < maxAttempts) {
        var result = callFn();
        if (result !== null) return result;
        gs.sleep(backoff);
        backoff *= 2;
        attempt++;
    }
    return null;
}

gs.sleep is acceptable inside a background script or a scheduled job. Avoid it inside a synchronous Business Rule — it will hold the transaction open and cause user-visible latency.

MID server: when to route through it

Route through MID server when:

  • The partner is on-premises and the platform cannot reach it directly
  • Compliance requires that the call originate from a known network egress point
  • The partner is rate-limiting by source IP and you need a stable one
  • You need to inspect the call from inside your network for debugging

Otherwise, route directly. MID server adds latency (the hop), adds operational concerns (the MID itself), and introduces a failure mode (the MID may be down even when the partner is up). Use it where you need it; do not use it everywhere because the architect-template-from-2019 said to.

Outbound logging discipline

sys_rest_message_log captures every V2 call when logging is enabled on the message record. Two warnings:

  • Logging is expensive at high volume. For a high-volume integration, log only on non-2xx or on a sampled fraction (every 100th call).
  • Logging captures the response body. If the body contains PII, you have just stored PII in the platform log. Either mask before logging or disable body logging entirely for that integration.

The right log retention for outbound calls is short — 30 to 90 days — because the use is debugging, not long-term audit. Long-term audit lives elsewhere.

A counter-example: when V2 hurts you

A team built a high-volume telemetry forwarder that sent device events to an external SIEM. They built it on V2 because the docs said to. At peak load the integration was using 40 percent of the node’s CPU on V2 parameter resolution and logging machinery. Rewriting the integration on plain sn_ws.RESTClient with bare-minimum logging cut the CPU usage to 8 percent. Same outcome, same partner, one tenth the cost.

The lesson is not that V2 is bad. The lesson is that defaults are defaults, and high-volume integrations deserve a measured decision.

UI implications for integration operators

The integration operator needs visibility. Build a saved list filtered to recent failed outbound requests, grouped by endpoint, with a count and the most recent error. Pin it to the platform ops dashboard. The first signal of an integration in trouble is usually a quiet uptick in non-2xx responses; visibility makes it actionable.

Pagination patterns

Partner APIs that return paged results are where many V2 integrations quietly break. The two common mistakes:

  • Assuming the page count fits in memory — the integration loads every page, accumulating into an in-memory array, and OOMs on the page where the dataset is unexpectedly large.
  • Trusting the partner’s has_more flag — some partner APIs lie about pagination boundaries in edge cases. Always also check the result count against an expected upper bound.

The pattern that works:

function fetchAllPages(endpoint, pageSize) {
    var page = 0;
    var processed = 0;
    var maxPages = 1000; // hard ceiling — never trust the partner alone
    while (page < maxPages) {
        var r = new sn_ws.RESTMessageV2('Partner', 'List');
        r.setStringParameterNoEscape('page', page);
        r.setStringParameterNoEscape('size', pageSize);
        var resp = r.execute();
        var data = JSON.parse(resp.getBody());
        if (!data.items || data.items.length === 0) break;
        processBatch(data.items); // stream, do not accumulate
        processed += data.items.length;
        if (!data.has_more) break;
        page++;
    }
    return processed;
}

processBatch writes each page to the platform incrementally. Memory stays bounded. The hard ceiling prevents an infinite loop if the partner’s pagination is buggy.

Connection alias hygiene

V2 uses connection and credential aliases to separate the endpoint configuration from the message records. Use them. The discipline:

  • Endpoints differ per environment (dev, test, prod). Aliases pull the right one based on the platform context.
  • Credentials differ per environment. Aliases pull the right one without exposing the credential value in the message record.
  • Promoting an integration from dev to prod becomes an alias-mapping change, not a message-record edit.

Without aliases, every environment promotion involves hand-editing message records, which is how prod ends up calling the dev endpoint at 3am.

Tradeoffs to be honest about

V2 is the right default. The “right default” is not the same as “always right.” When the integration is high-volume, latency-sensitive, or has unusual auth, the savings from dropping to plain HTTP are real. Document the choice; do not let it be invisible.

V1 is not deprecated. It is also not a place to invest in new integrations. If you are starting fresh, start on V2 unless there is a specific reason not to.

Key takeaways

  • RESTMessageV2 is the right default for most integrations. The right default is not the same as the right answer for every case.
  • Always set an explicit timeout, a correlation ID, status-code checking, and a try/catch on every V2 call.
  • Plain sn_ws.RESTClient wins for high-volume, latency-sensitive, or non-standard-auth integrations. Document the choice.
  • Never retry non-idempotent writes without an idempotency key. Duplicate invoices are forever.
  • Logging is expensive at high volume and may capture PII. Configure body logging deliberately, not by default.
[object Object]
Share