SDS Borrower API

Create and retrieve borrowers, generate action plans, and hand agency-hosted borrowers into the SDS Borrower Portal.

Development base URL: https://sds-borrower-api-dev.efiscal.net

Open Swagger Webhook reference

Quick start

  1. Exchange your SDS API username and password for a bearer token.
  2. Call GET /api/Me to discover the channels available to that credential.
  3. Use a channel returned by /api/Me, then validate that the selected channel is active.
  4. Create either a standard SDS-hosted borrower or an agency-hosted borrower.
  5. Store the returned borrower ID. Use it for borrower lookup and action-plan requests.
Method Route Purpose Bearer token
POST/api/AuthenticationGet an access tokenNo
GET/api/MeGet the credential's channelsYes
POST/api/Borrowers/ValidateCheck the selected channelYes
POST/api/BorrowersCreate a standard borrowerYes
POST/api/Borrowers/CreateAgencyHostedBorrowerCreate an agency-hosted borrowerYes
GET/api/Borrowers?id={borrowerId}Retrieve a borrowerYes
POST/api/ActionPlanGet an action plan as PDF or HTMLYes
POST/api/Borrowers/CreateAttestationBorrowerCreate a specialized attestation borrowerYes

1. Authenticate

Authenticate from your server. The response is HTTP 200 even when authentication fails, so check success and confirm that token is present.

POST /api/Authentication

Request
curl --request POST \
  https://sds-borrower-api-dev.efiscal.net/api/Authentication \
  --header "Content-Type: application/json" \
  --data '{
    "username": "<agency-api-username>",
    "password": "<agency-api-password>"
  }'
Successful response
{
  "token": "eyJraWQiOi...",
  "success": true,
  "message": "Success",
  "expiresIn": 3600
}

Send the token on subsequent API requests as Authorization: Bearer <access-token>. The sample lifetime is illustrative; honor the actual expiresIn value and re-authenticate before the token expires.

2. Discover and validate a channel

A borrower belongs to one channel. Agency-level credentials can return multiple channels; channel-level credentials return only their assigned channel.

Get authorized channels
curl https://sds-borrower-api-dev.efiscal.net/api/Me \
  --header "Authorization: Bearer <agency-access-token>"
Example response
{
  "username": "agency.integration",
  "channels": [
    {
      "name": "Primary Channel",
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    }
  ]
}

Check the selected channel before creation. Always select it from /api/Me; this validation call is not a substitute for channel discovery.

Validate
curl --request POST \
  https://sds-borrower-api-dev.efiscal.net/api/Borrowers/Validate \
  --header "Authorization: Bearer <agency-access-token>" \
  --header "Content-Type: application/json" \
  --data '{
    "username": "sample.borrower@example.com",
    "channelId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  }'

A successful response is { "success": true, "message": null }. For a channel-scoped credential, the action rejects a different channel. For an agency-scoped credential, it currently checks only that the channel exists and is active. The action does not inspect username or check username availability.

3. Create a standard SDS-hosted borrower

Use this path when SDS owns the borrower's sign-in experience. SDS creates the identity and sends the borrower an invitation.

Supply name, email, username, and channelId. Address and phone are optional; the example includes both.

POST /api/Borrowers

Request
curl --request POST \
  https://sds-borrower-api-dev.efiscal.net/api/Borrowers \
  --header "Authorization: Bearer <agency-access-token>" \
  --header "Content-Type: application/json" \
  --data '{
    "address": {
      "address1": "100 Main Street",
      "address2": null,
      "city": "Columbus",
      "state": "OH",
      "zip": "43215"
    },
    "name": {
      "firstName": "Sample",
      "lastName": "Borrower"
    },
    "email": "sample.borrower@example.com",
    "ignoreEmailValidation": false,
    "phone": {
      "number": "6145550100",
      "isMobile": true
    },
    "channelId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "username": "sample.borrower@example.com",
    "acceptTCPA": true
  }'

A successful response is HTTP 200 with the borrower ID as a JSON string: "c2ec899e-79d3-4a63-a31c-5d849b6bb66c".

4. Create and sign in an agency-hosted borrower

Use this path when the agency owns the borrower-facing application and credentials. SDS creates the borrower with the supplied permanent password and does not send an invitation.

Supply the same borrower identity and channel fields as standard creation, plus a password that satisfies the environment's SDS Cognito password policy. Address and phone remain optional.

POST /api/Borrowers/CreateAgencyHostedBorrower

Create borrower
curl --request POST \
  https://sds-borrower-api-dev.efiscal.net/api/Borrowers/CreateAgencyHostedBorrower \
  --header "Authorization: Bearer <agency-access-token>" \
  --header "Content-Type: application/json" \
  --data '{
    "address": {
      "address1": "100 Main Street",
      "address2": null,
      "city": "Columbus",
      "state": "OH",
      "zip": "43215"
    },
    "name": {
      "firstName": "Sample",
      "lastName": "Borrower"
    },
    "email": "sample.borrower@example.com",
    "phone": {
      "number": "6145550100",
      "isMobile": true
    },
    "channelId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "username": "sample.borrower@example.com",
    "password": "<strong-borrower-password>"
  }'

A successful response is the borrower ID as a JSON string. Store it before attempting the portal handoff.

Optional: hand the borrower into the Borrower Portal

The handoff uses the borrower's JWT, not the agency integration JWT. Your server authenticates with the newly created borrower's username and password, then renders an auto-submitting form in that borrower's browser.

The token must be an SDS Cognito access token issued for the borrower in the matching environment. The portal does not accept an agency-issued or other third-party JWT.

  1. From the agency server, call POST /api/Authentication with the borrower credentials.
  2. Verify success, then place the returned borrower token in a short-lived server-rendered form.
  3. Navigate the browser by POSTing the form to the Borrower Portal's /api/ExternalLogin endpoint.
Authenticate as the borrower
curl --request POST \
  https://sds-borrower-api-dev.efiscal.net/api/Authentication \
  --header "Content-Type: application/json" \
  --data '{
    "username": "sample.borrower@example.com",
    "password": "<strong-borrower-password>"
  }'
Server-rendered browser handoff
<form id="sds-login"
      method="post"
      action="https://sds-borrower-ui-dev.efiscal.net/api/ExternalLogin">
  <input type="hidden" name="Token" value="<borrower-access-token>" />
  <input type="hidden" name="ReturnUrl" value="/" />
  <noscript><button type="submit">Continue to SDS</button></noscript>
</form>
<script>document.getElementById("sds-login").submit();</script>

HTML-attribute-encode the token when rendering the form. Use a predetermined, local Borrower Portal path such as / for ReturnUrl; never relay a user-supplied return URL. The final request must be a browser navigation so the portal can establish its authentication cookie.

5. Retrieve a borrower

Request
curl "https://sds-borrower-api-dev.efiscal.net/api/Borrowers?id=<borrower-id>" \
  --header "Authorization: Bearer <agency-access-token>"
Abbreviated response
{
  "pkId": "c2ec899e-79d3-4a63-a31c-5d849b6bb66c",
  "name": {
    "firstName": "Sample",
    "lastName": "Borrower"
  },
  "email": "sample.borrower@example.com",
  "status": 0,
  "lastStepCompleted": 0,
  "planSummary": null
}

6. Get an action plan

Set actionPlanFormat to 1 for PDF or 2 for HTML. PDF content is base64 encoded in base64String; HTML content is returned directly in that same property.

Request a PDF
curl --request POST \
  https://sds-borrower-api-dev.efiscal.net/api/ActionPlan \
  --header "Authorization: Bearer <agency-access-token>" \
  --header "Content-Type: application/json" \
  --data '{
    "actionPlanFormat": 1,
    "borrowerId": "c2ec899e-79d3-4a63-a31c-5d849b6bb66c"
  }'
Response shape
{
  "success": true,
  "message": "Action Plan succeeded",
  "dateTime": "0001-01-01T00:00:00",
  "base64String": "JVBERi0xLjQ..."
}

7. Create an attestation borrower

This is a specialized, channel-scoped integration. Coordinate the attestation schema and enum values with SDS before implementation. The nested attestation payload is passed as a serialized JSON string.

Use a channel-scoped API credential. The current action derives the channel from that credential; omit channelId from the request unless SDS directs otherwise. Borrower family size is derived from the household members in attestationDataJson.

Request shape
{
  "address": {
    "address1": "100 Main Street",
    "city": "Columbus",
    "state": "OH",
    "zip": "43215"
  },
  "name": {
    "firstName": "Sample",
    "lastName": "Borrower"
  },
  "email": "sample.borrower@example.com",
  "ignoreEmailValidation": false,
  "phone": {
    "number": "6145550100",
    "isMobile": true
  },
  "username": "sample.borrower@example.com",
  "attestationDataJson": "{\"...\":\"SDS-provided attestation fields\"}",
  "sendInvitation": true,
  "taxFilingStatus": 1
}

The endpoint is POST /api/Borrowers/CreateAttestationBorrower. Its response contains borrowerId, success, and message.

8. Subscribe to borrower events

Agencies can register HTTPS listeners for borrower creation and status changes, intake progress, counseling, document preparation, and action-plan events. Subscriptions may be scoped to an entire agency or selected channels.

View webhook events and payload examples

Plan your integration with SDS

This guide explains the supported flow and provides representative examples. Before implementation, review these decisions with SDS so both teams have the same expectations:

  • Which borrower-creation path fits the experience: standard SDS-hosted, agency-hosted, or the specialized attestation flow?
  • Which development and production environments, agency, and channels are in scope?
  • For agency-hosted borrowers, will users remain in the agency application or be handed into the SDS Borrower Portal?
  • If the portal handoff is used, where should SDS send the borrower after login?
  • Are borrower lookup, action-plan retrieval, or webhooks part of the first release?
  • Who are the technical and operational contacts for testing, production onboarding, and support?

SDS supplies environment-specific URLs, credentials, channel access, and any specialized schemas during onboarding. Do not place those values in shared documentation or source control.

Errors, retries, and security

  • Use TLS and make API calls from a trusted server, not public browser JavaScript.
  • Keep agency credentials, borrower passwords, and JWTs out of URLs, logs, analytics, and source control.
  • Check both the HTTP status and response body. Some endpoints report business failure in a 200 response.
  • Treat borrower creation as non-idempotent. After a timeout, validate or look up state before retrying to avoid duplicate users.
  • Use only channel IDs returned for the authenticated agency credential.
  • Treat borrower IDs as sensitive identifiers and authorize their use within your own application.
  • Do not assume diagnostic endpoints visible in Swagger are supported agency integration operations.

Swagger is the source for the currently deployed request and response schema. This page provides the recommended integration flow.