Integration

Implement a referral redirect and S2S postback for loan leads

A referral handoff has two legs: a redirect that carries signed parameters and a server-to-server postback that returns the outcome. This guide covers storage, signature checks, statuses and retries.

Updated 29 October 2026

Referral delivery has a redirect leg and a postback leg

In referral delivery, an applicant who passes screening is redirected to your flow with parameters that identify the click. The applicant applies on your site, and later your server sends a postback with the outcome. The click ID joins the two legs. Because the S2S postback runs server to server, it does not depend on the applicant's browser.

  1. 1CYBERTECH
    Applicant passes screening
    Screened against the lender's rules before the redirect.
  2. Redirect
    Through the applicant's browser
    click_id
    ct_9f2a61c4
    sub_id
    srch_pl_07
    utm_source
    cybertech
    sig
    5be0c1…e1a4
  3. 2Lender
    Applicant applies on the lender's site
    The lender stores the parameters and keeps the credit decision.
  4. 3Lender
    Server sends the outcome
    Once the status is final, never from the browser.
  5. Postback
    Outcome, server to server
    click_id
    ct_9f2a61c4
    status
    approved
    event_time
    2026-10-27T09:14:03Z
  6. 4CYBERTECH
    Outcome is recorded
    Matched to the click by click ID, then used to tune the checks.
Illustrative referral handoff with fictional values. Parameter names, statuses and the signature scheme are agreed at onboarding.

CYBERTECH records each outcome, and returned outcomes help tune the checks over time. The integration page compares this mode with the direct API.

Parameter names, status values and the signature scheme here are illustrative. They are agreed during onboarding, before launch, and that agreement takes precedence.

Redirect parameters arrive in the query string

In a branded redirect, the applicant's browser makes an HTTP GET to your landing URL. UTM and sub-id values pass through unchanged. If you use a hosted portal instead, the handoff is agreed at onboarding.

ParameterWhat it carriesWhat to do with it
click_idThe identifier of this click, and the join key for every later status.Store it against the application and treat it as an opaque string: never parse, truncate or change its case.
sub_idThe traffic sub-source behind the click.Keep it on the application to compare outcomes by source.
utm_sourceCampaign attribution in UTM form.Pass it to your analytics unchanged.
sigA signature over the signed parameters, which are agreed at onboarding.Verify it before you trust the rest.

Illustrative parameters for a referral redirect.

Referral redirectIllustrative
# 1  The applicant leaves with signed parameters
GET https://apply.lender.test/start
    ?click_id=ct_9f2a61c4
    &sub_id=srch_pl_07
    &utm_source=cybertech
    &sig=5be0c1…e1a4

Store the parameters before the applicant starts

Your server sends the postback later, so persist what you need when the redirect lands.

  • The raw query string as received, for debugging.
  • click_id, sub_id and utm_source, saved against your application record and kept until the last status you have agreed to send.
  • The result of the signature check and the arrival time in UTC.

Verify the signed parameters before you trust them

The signature lets you confirm that CYBERTECH issued the parameters and nobody edited them. A common design is a shared secret key: the sender computes a keyed hash over the parameters, and the receiver recomputes it and compares (RFC 2104 describes one construction, HMAC). Whether your setup uses a shared key, and its construction, parameter order and encoding, are agreed at onboarding.

  1. Capture the parameters as receivedRead the raw query string before your framework decodes it. If the signature covers the received characters, re-encoding changes what was signed: RFC 3986 treats percent-encodings that differ only in hexadecimal case as equivalent, but a signature does not. If your setup signs decoded values instead, step 2 governs.
  2. Rebuild the signed stringAssemble it as agreed: which parameters, in which order, with which separators. Compute the keyed hash with the key agreed for your setup, and keep that key out of front-end code.
  3. Compare without a timing leakUse a comparison designed to resist timing analysis instead of ==. In Python that is hmac.compare_digest, which takes ASCII-only strings or bytes, so compare bytes, such as hmac.compare_digest(expected.encode(), received.encode()): a stray non-ASCII character in sig then fails the check instead of raising an error.
  4. Decide what a mismatch meansDo not attribute the click or send postbacks for it. Log the request. Whether the applicant may continue is your decision.

A valid signature shows the parameters are unaltered, not that the link is new: a copy of a signed link verifies every time. Ask at onboarding whether the signed string includes a timestamp, and review any click_id that starts more than one application.

Send the outcomes you agreed at onboarding

Test the status that matches your billable event first, because it decides what is billed; pricing explains how that event is defined.

StatusMeaningSend when
submittedApplication completed.Your system accepts it.
approvedPositive credit decision.The decision is final.
rejectedApplication declined.The decision is final, with a reason_code.
disbursedFunds released.Disbursal is confirmed.

Illustrative status names. The list, the exact values and any reversal status are agreed at onboarding.

A rejected postback carries a reason_code: a code from the rejection list agreed at onboarding, never free text. Pricing shows the kinds of reason a contract needs. A reversal works the same way. If a status is reversed inside the validation window, send the agreed reversal status with its reason_code for the same click_id. Whether a reversal sent after the window closes still applies is agreed in advance.

Send the postback from your server when a status changes in your system of record, not on a timer and not from the browser, and only once the status is final. Set event_time to the moment the status changed, not the send time, in UTC with a trailing Z and the same number of fractional digits, so the strings sort in time order (RFC 3339, section 5.1). Use HTTPS, and ask how postbacks are authenticated.

Outcome postbackIllustrative
# 2  Your server confirms the outcome
GET {agreed postback URL}
    ?click_id=ct_9f2a61c4
    &status=approved
    &event_time=2026-10-27T09:14:03Z

# ← 200 OK

# status=rejected also sends &reason_code=, a code from the agreed list

Plan for responses, retries and duplicates

  • A 2xx response means the postback was received and accepted (RFC 9110). Stop retrying on the first one.
  • Retry after a timeout, a dropped connection, or a 500, 502, 503 or 504 response. Wait longer after each attempt and add a random delay, so retries do not arrive in bursts (Amazon Web Services). Honour any Retry-After header (RFC 9110, section 10.2.3).
  • Cap the attempts or the total time, then queue the unsent status and alert someone, so a billable outcome is replayed rather than lost.
  • Do not resend a 4xx request unchanged: that class means the client seems to have erred (RFC 9110, section 15.5). The usual exceptions are 408, where the client may repeat the request (section 15.5.9), and 429, which signals rate limiting.
  • Expect duplicates. A sender that loses the connection cannot know whether the first request was applied, so HTTP allows repeating an idempotent request even if the original succeeded (RFC 9110, section 9.2.2). A common design counts the first accepted click_id and status pair and answers later copies with a success code, which ends your retries; agree the rule at onboarding.
  • Expect late postbacks. Statuses can arrive late or out of order, so event_time carries the moment of the status change. Agree how the receiving side treats them.

Test every status before launch

Agree with your CYBERTECH contact at onboarding how to test, then run the full sequence before going live.

  • A redirect with a valid signature: your landing page stores every parameter in the table above.
  • A redirect with one parameter edited: verification fails and the visit is not attributed.
  • One postback per agreed status, with a reason_code on rejected and on any reversal, each response recorded and checked.
  • The same postback twice: the result matches the duplicate rule you agreed, and your sender stops retrying on the first 2xx.
  • Statuses out of order, for instance disbursed before approved: the result matches the ordering rule you agreed.
  • A 503 forced on the first attempt from a stub endpoint of your own: your sender retries with a growing wait and stops after the first 2xx.

Keep direct identifiers out of postbacks

We recommend that postbacks carry identifiers and statuses only: click_id, status and event_time match an outcome to a click, plus a reason_code on a rejection or reversal. Leave out names, phone numbers, email addresses, government ID numbers and free-text decline reasons. Even this set can identify an applicant to a partner that links the click_id to a person, so which statuses you send depends on your notice, the borrower's consent and local law; pricing gives the India rule.

A postback is a URL, and a query string can be stored in web logs. MITRE records sensitive data in a query string as a weakness (CWE-598), and NIST calls minimising the use, collection and retention of personal data a basic privacy principle (SP 800-122).

Check these causes first when the handoff fails

SymptomLikely cause and fix
click_id is missing on the landing pageA redirect in your chain, such as HTTP to HTTPS or a sign-in hop, dropped the query string, or the parameters sit after a #, which never reaches your server (RFC 9110). Make every hop pass the query on.
The signature never verifiesThe query string was re-encoded before the check, the signed string differs from what you agreed, or the key is outdated. Check the raw query string and confirm the key with your CYBERTECH contact.
Postbacks get a 4xx responseTemplate fields never filled, a value that is not percent-encoded (RFC 3986), a status or reason_code that was not agreed, or a click_id altered in case or length. Send values exactly as stored.
A postback returns 200 but the outcome is missingThe sender may call a different URL from the agreed one or send an altered click_id. Log each request with its response and match it to the stored click.
Statuses appear in the wrong orderevent_time holds the send time or is not in UTC. Take it from the status change in your system, with a trailing Z.

With referral working, the anti-fraud page describes the checks that returned outcomes help tune. To start on your parameters and statuses, contact us.

Sources

  1. IETF: RFC 2104, HMAC: Keyed-Hashing for Message Authentication (1997)
  2. IETF: RFC 9110, HTTP Semantics (2022)
  3. IETF: RFC 6585, Additional HTTP Status Codes (2012)
  4. IETF: RFC 3339, Date and Time on the Internet: Timestamps (2002)
  5. IETF: RFC 3986, Uniform Resource Identifier (URI): Generic Syntax (2005)
  6. MITRE: CWE-598, Use of HTTP Request With Sensitive Query String (2026)
  7. NIST: SP 800-122, Guide to Protecting the Confidentiality of Personally Identifiable Information (2010)
  8. Python Software Foundation: hmac, Keyed-Hashing for Message Authentication (Python 3 documentation, 2026)
  9. Amazon Web Services: Timeouts, retries, and backoff with jitter (2019)

Agree your parameters and statuses

Tell us your market, your product and the outcomes your system can report. We will come back with parameters, statuses and a test sequence to agree.

Discuss your integration