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
Quick start
- Exchange your SDS API username and password for a bearer token.
- Call
GET /api/Meto discover the channels available to that credential. - Use a channel returned by
/api/Me, then validate that the selected channel is active. - Create either a standard SDS-hosted borrower or an agency-hosted borrower.
- Store the returned borrower ID. Use it for borrower lookup and action-plan requests.
| Method | Route | Purpose | Bearer token |
|---|---|---|---|
POST | /api/Authentication | Get an access token | No |
GET | /api/Me | Get the credential's channels | Yes |
POST | /api/Borrowers/Validate | Check the selected channel | Yes |
POST | /api/Borrowers | Create a standard borrower | Yes |
POST | /api/Borrowers/CreateAgencyHostedBorrower | Create an agency-hosted borrower | Yes |
GET | /api/Borrowers?id={borrowerId} | Retrieve a borrower | Yes |
POST | /api/ActionPlan | Get an action plan as PDF or HTML | Yes |
POST | /api/Borrowers/CreateAttestationBorrower | Create a specialized attestation borrower | Yes |
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
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>"
}'
{
"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.
curl https://sds-borrower-api-dev.efiscal.net/api/Me \ --header "Authorization: Bearer <agency-access-token>"
{
"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.
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
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
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.
- From the agency server, call
POST /api/Authenticationwith the borrower credentials. - Verify
success, then place the returned borrower token in a short-lived server-rendered form. - Navigate the browser by POSTing the form to the Borrower Portal's
/api/ExternalLoginendpoint.
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>"
}'
<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
curl "https://sds-borrower-api-dev.efiscal.net/api/Borrowers?id=<borrower-id>" \ --header "Authorization: Bearer <agency-access-token>"
{
"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.
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"
}'
{
"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.
{
"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 examplesPlan 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.