NAV

Version: ClientApi v1.2.1 https://api.sltt-bible.net

Introduction

Welcome to the SLTT Client API!

This API documentation page was created with Slate.

Your org is your partner

In the SLTT Client API, your organization is referred to as your partner. So, when you see partnerId in the documentation, it refers to your organization's id in SLTT. Usually this is the primary email domain name for your organization. Most endpoints will be based on /partner or /batch/partner endpoints, indicating that the data is filtered by your partner's projects and roles.

Authentication (Basic Auth)

SLTT Client API authentication is basic authentiaton based on a public/secret token pair associated with your client and your organization (remember, in this api your organization referred to as your partner).

curl --request GET \
  --url "api_endpoint_here" \
  -u {client_id}:{token}

If you haven't already received a clientId and token pair, please see Appendix 2: Apply for an api token.

All requests to the SLTT Client API require basic authentication using your clientId and token pair.

The clientId should be the basic auth user and the token will be the basic auth password.

For example, if you have a clientId

client--createdAt--1696539988381--partnerId--example.com

and a token

aeb433366bce021414c288bb9e5ff992

for curl you would join them with a colon (:) to form the basic auth -u user:password credentials:

-u client--createdAt--1696539988381--partnerId--example.com:aeb433366bce021414c288bb9e5ff992

for other REST clients, you may need to base 64 encode the user:password joined string for the Authorization header:

{ "Authorization": "Basic Y2xpZW50LS1jcmVhdGVkQXQtLTE2OTY1Mzk5ODgzODEtLXBhcnRuZXJJZC0tZXhhbXBsZS5jb206YWViNDMzMzY2YmNlMDIxNDE0YzI4OGJiOWU1ZmY5OTI=" }

Get api docs (no auth)

This provides this client api documentation

HTTP Request

curl --request GET \
  --url https://api.sltt-bible.net

GET https://https://api.sltt-bible.net

Response Example

(These api docs)

Get projects and partner roles

This provides a simple listing of project ids and the roles that your partnerId plays in those projects

HTTP Request

curl --request GET \
  --url https://api.sltt-bible.net/partner/projects \
  -u client--createdAt--1696539988381--partnerId--example.com:aeb433366bce021414c288bb9e5ff992

GET https://https://api.sltt-bible.net/partner/projects -u {clientId}:{token}

Response Example

[
    {
        "partnerId": "your.example.partner-id",
        "projectId": "YourExampleProject",
        "roles": [
            "consultant"
        ],
        "source": "system"
    }
]

Response Schema

type GetPartnerProjectsResponse = {
    projectId: string
    partnerId: string
    roles: PartnerRole[]
    source: PartnerSource
}[]

enum PartnerRole {
    admin = ProjectMemberRole.admin,
    translator = ProjectMemberRole.translator,
    consultant = ProjectMemberRole.consultant,
    interpreter = ProjectMemberRole.interpreter,
    observer = ProjectMemberRole.observer,
    sysops = 'sysops',  // SLTT system/sysops for (root admins)
}

// system or a domain export that provided the role data
enum PartnerSource {
    system = 'system',
    stuartt = 'stuartt@doorinternational.org',
    esampson = 'esampson@biblesocieties.org'
}

enum ProjectMemberRole {
    admin = 'admin',
    translator = 'translator',
    consultant = 'consultant',
    interpreter = 'interpreter',
    observer = 'observer',
}

Batch get project metadata

This provides basic project metadata for all the projects that your partnerId is associated with

HTTP Request

curl --request GET \
  --url https://api.sltt-bible.net/batch/partner/projects/metadata \
  -u client--createdAt--1696539988381--partnerId--example.com:aeb433366bce021414c288bb9e5ff992

GET https://https://api.sltt-bible.net/batch/partner/projects -u {clientId}:{token}

NOTE: /batch/ endpoints may require pagination of projectIds if there are results from more than 100 projects. See Appendix 1: Request Pagination section below for details.

Response Example

{
    "batchSummary": {
        "partnerId": "your.example.partner-id",
        "unprocessed": {
            "count": 0,
            "queryParams": {
                "batchGetKeyLimit": 100,
                "unprocessedKeys": "",
            }
        },
        "processed": {
            "count": 1,
            "projectIds": "YourExampleProject1,YourExampleProject2"
        }
    },
    "batchResponse": [
        {
            "name": "YourExampleProject1",
            "displayName": "Your Example Project",
            "description": "",
            "region": "africa",
            "projectType": "translation",
            "language": "",
            "lastUpdated": "2023/02/09 12:17:01.975Z",
            "plannedCompletionDate": "",
            "progress": 0,
            "lastVideoAt": "2023-12-13T08:38:16.351Z",
            "lastNoteAt": "2024-01-12T09:22:28.318Z",
            "totalVideos": 5,
            "totalNotes": 48,
            "activityStatus": "trained",
            "teamId": "Egypt-1"
        },
        //...
    ]
}

Response Schema

type GetBatchPartnerProjectsMetadataResponse = {
    batchSummary: {
        partnerId: string,
        unprocessed: {
            count: number,
            queryParams: {
                batchGetKeyLimit: number /* default is 100 */,
                unprocessedKeys: CommaSeparatedListString /* query parameter and value for pagination */,
            }
        },
        processed: {
            count: number,
            projectIds: CommaSeparatedListString,
        },
    },
    batchResponse: ProjectMetadataBasic[]
}

type ProjectMetadataBasic = {
    name: string
    displayName: string
    description: string
    language: string // (currently unused)
    progress: number // % total project complete, e.g. '33.1'
    plannedCompletionDate: string  // yyyy-mm-dd (currently unused)
    lastUpdated: string  // yyyy-mm-dd
    lastVideoAt?: string // ISO Date/Time
    lastNoteAt?: string // ISO Date/Time
    activityStatus: ProjectStatus
    totalVideos: number
    totalNotes: number
    projectType: ProjectType
    region: Region
    teamId: string // can be used to group projects
}

enum ProjectStatus {
    inactive = 'inactive' /* never trained and no recent note or video activity */,
    trained = 'trained' /* more than 20 notes or videos posted */,
    training = 'training' /* has recent note or video activity */,
    active = 'active' /* is trained and has recent note or video activity */,
}

type ProjectType = 'translation' | 'additional' | 'test_training' | 'resource' | 'other' | ''
type Region = 'africa' | 'americas' | 'asia' | 'europe' | 'oceania' | ''

type CommaSeparatedListString = string /* comma separated list in string "a,b,c" */

Batch get partners

This provides a list of all the partners (and their project roles) that your (partner's) projects have

HTTP Request

curl --request GET \
  --url https://api.sltt-bible.net/batch/partner/partners \
  -u client--createdAt--1696539988381--partnerId--example.com:aeb433366bce021414c288bb9e5ff992

GET https://https://api.sltt-bible.net/batch/partner/partners -u {clientId}:{token}

Response Example

{
    "batchSummary": {
        "partnerId": "system",
        "unprocessed": {
            "count": 0,
            "queryParams": {
                "batchGetKeyLimit": 200,
                "unprocessedKeys": "",
            }
        },
        "processed": {
            "count": 2,
            "roles": "admin,consultant,interpreter,observer,sysops,translator",
            "partnerIds": "wycliffebenin.org,partner.2",
            "projectIds": "YourExampleProject,YourExampleProject2",
            "bytes": 46507,
            "timeTaken": 501
        }
    },
    "batchResponse": [
        {
            "projectId": "YourExampleProject",
            "partnerId": "system",
            "roles": [
                "sysops"
            ]
        },
        {
            "projectId": "YourExampleProject",
            "partnerId": "your.example.partner-id",
            "roles": [
                "consultant"
            ]
        },
        //...
    ],
}

Response Schema

export type GetBatchPartnerProjectsPartnersResponse = {
    batchSummary: {
        partnerId: string,
        unprocessed: {
            count: number,
            projectIds: CommaSeparatedListString,
            queryParams: {
                batchGetKeyLimit: number,
                unprocessedKeys: CommaSeparatedListString,
            }
        },
        processed: {
            count: number,
            roles: CommaSeparatedListString,
            partnerIds: CommaSeparatedListString,
            projectIds: CommaSeparatedListString,
            bytes: number,
        },
    },
    batchResponse: GetProjectPartnersResponse[]
}


type ProjectPartnersItem = {
    partnerId: string,
    projectId: string,
    roles: PartnerRole[],
    source: PartnerSource,
}

export type GetProjectPartnersResponse = ProjectPartnersItem[]

enum PartnerRole {
    admin = ProjectMemberRole.admin,
    translator = ProjectMemberRole.translator,
    consultant = ProjectMemberRole.consultant,
    interpreter = ProjectMemberRole.interpreter,
    observer = ProjectMemberRole.observer,
    sysops = 'sysops',  // SLTT system/sysops for (root admins)
}

enum ProjectMemberRole {
    admin = 'admin',
    translator = 'translator',
    consultant = 'consultant',
    interpreter = 'interpreter',
    observer = 'observer',
}

type CommaSeparatedListString = string /* comma separated list in string "a,b,c" */

Batch get passages

This provides a list of all the passages (and their tasks) that your (partner's) projects have

HTTP Request

curl --request GET \
  --url https://api.sltt-bible.net/batch/partner/passages \
  -u client--createdAt--1696539988381--partnerId--example.com:aeb433366bce021414c288bb9e5ff992

GET https://https://api.sltt-bible.net/batch/partner/passages -u {clientId}:{token}

NOTE: The cursor query parameter should be used with the GET /partner/projects/{projectsId}/passages endpoint to paginate through a project's passages if GET /batch/partner/passages returns its projectId in batchSummary.unprocessed.projectIdsWithCursors and its cursor is present in its batchResponse[].summary.unprocessed.queryParams.cursor

ALSO IMPORTANT: The client will be responsible for merging the paginated passages data from the different requests. Also, the summary shows the passages/portions for the paginated data, not all the passages/portions for the project.

Response Example

{
    "batchSummary": {
        "partnerId": "system",
        "unprocessed": {
            "count": 1,
            "projectIdsWithCursors": [],
            "queryParams": {
                "batchGetKeyLimit": 2,
                "unprocessedKeys": "YourExampleProject3"
            }
        },
        "processed": {
            "count": 2,
            "projectIds": "YourExampleProject1,YourExampleProject2",
            "bytes": 90615,
            "timeTaken": 504
        }
    },
    "batchResponse": [
        {
            "summary": {
                "partnerId": "system",
                "projectId": "YourExampleProject1",
                "unprocessed": {
                    "queryParams": {
                        "cursor": null
                    }
                },
            "processed": {
                    "stagesCount": 4,
                    "tasksCount": 8,
                    "taskDifficulties": [
                        " 0,1,1,1,1,1,1,0"
                    ],
                    "passageCounts": [
                        "64,0,0,0,0,0,0,0"
                    ],
                    "passageCountsByPortion": [
                        " 4,0,0,0,0,0,0,0 - KGH 05",
                        " 4,0,0,0,0,0,0,0 - KGH 06",
                        " 4,0,0,0,0,0,0,0 - KGH 07",
                        " 4,0,0,0,0,0,0,0 - KGH 08",
                        " 4,0,0,0,0,0,0,0 - KGH 09",
                        " 4,0,0,0,0,0,0,0 - KGH 10",
                        " 4,0,0,0,0,0,0,0 - KGH 11",
                        " 4,0,0,0,0,0,0,0 - KGH 12",
                        " 4,0,0,0,0,0,0,0 - KGH 13",
                        " 4,0,0,0,0,0,0,0 - KGH 16",
                        " 4,0,0,0,0,0,0,0 - KGH 17",
                        " 4,0,0,0,0,0,0,0 - KGH 18",
                        " 4,0,0,0,0,0,0,0 - KGH 19",
                        " 4,0,0,0,0,0,0,0 - KGH 20",
                        " 4,0,0,0,0,0,0,0 - KGH 22",
                        " 4,0,0,0,0,0,0,0 - KGH 31 B"
                    ],
                    "portionsCount": 16,
                    "passagesCount": 64,
                    "passagesInProgressCount": 0,
                    "bytes": 11998
                }
            },
            "response": {
                "project": {
                    "name": "YourExampleProject1",
                    "displayName": "Your Example Project 1",
                    "description": "",
                    "language": "",
                    "progress": 0,
                    "plannedCompletionDate": "",
                    "lastUpdated": "2023/02/09 12:17:01.975Z",
                    "lastVideoAt": "2023-12-13T08:38:16.351Z",
                    "lastNoteAt": "2024-01-12T09:22:28.318Z",
                    "activityStatus": "trained",
                    "totalVideos": 5,
                    "totalNotes": 48,
                    "projectType": "translation",
                    "region": "africa",
                    "teamId": "YourTeamId"
                },
                "tasks": [
                    {
                        "taskId": "Not started",
                        "taskName": "Not started",
                        "bars": "|◇ ◻ ◻ ◻ ◻ ◻ ◻ ◇      0%",
                        "difficulty": 0,
                        "difficultyPercent": 0,
                        "stageId": "Not started"
                    },
                    {
                        "taskId": "1.1",
                        "taskName": "First Draft",
                        "bars": " ◇|◻ ◻ ◻ ◻ ◻ ◻ ◇      0%",
                        "difficulty": 1,
                        "difficultyPercent": 16.67,
                        "stageId": "1"
                    },
                    {
                        "taskId": "1.2",
                        "taskName": "Review First Draft",
                        "bars": " ◇ ◼|◻ ◻ ◻ ◻ ◻ ◇    16.67%",
                        "difficulty": 1,
                        "difficultyPercent": 16.67,
                        "stageId": "1"
                    },
                    //...
                    {
                        "taskId": "2.2",
                        "taskName": "Third Draft",
                        "bars": " ◇ ◼ ◼ ◼ ◼ ◼|◻ ◇    83.33%",
                        "difficulty": 1,
                        "difficultyPercent": 16.67,
                        "stageId": "2"
                    },
                    {
                        "taskId": "Finished",
                        "taskName": "Finished",
                        "bars": " ◇ ◼ ◼ ◼ ◼ ◼ ◼|✓    100%",
                        "difficulty": 0,
                        "difficultyPercent": 0,
                        "stageId": "Finished"
                    }
                ],
                "passages": [
                    {
                        "entityId": "230209_114806/230209_120913",
                        "passageName": "01.Topic",
                        "taskId": "Not started",
                        "bars": "|◇ ◻ ◻ ◻ ◻ ◻ ◻ ◇      0%",
                        "difficulty": 1,
                        "portionName": "KGH 05",
                        "scripture": "GEN 7:1-24"
                    },
                    //...
                ],
            },
        },
    ]
}

Response Schema

export type GetBatchPartnerPassagesResponse = {
    batchSummary: {
        partnerId: string,
        unprocessed: {
            count: number,
            projectIdsWithCursors: string[],
            queryParams: {
                eachGetItemsLimit: number,
                batchGetKeyLimit: number,
                unprocessedKeys: CommaSeparatedListString,
            }
        },
        processed: {
            count: number,
            projectIds: CommaSeparatedListString,
            bytes: number,
            timeTaken: number /* in milliseconds */,
        }
    },
    batchResponse: GetPartnerProjectPassagesResponse[]
}

export type GetPartnerProjectPassagesResponse = {
    summary: {
        partnerId: string,
        projectId: string,
        unprocessed: {
            queryParams: {
                getItemsLimit: number,
                cursor: string,
            }
        },
        processed: {
            stagesCount: number,
            tasksCount: number,
            taskDifficulties: string[],
            passageCounts: string[],
            passageCountsByPortion: string[],
            portionsCount: number,
            passagesCount: number,
            passagesInProgressCount: number,
            bytes: number,
        },
    },
    response: PartnerProjectPassagesResponse
}

type PartnerProjectPassagesResponse = {
    project: ProjectMetadataBasic,
    tasks: ProjectTaskForApi[],
    passages: ProjectPassageForApi[],
}

export type ProjectTaskForApi = {
    taskId: string
    taskName: string
    bars: string // " ◇ ◼ ◼ ◼ ◼ ◼|◻ ◇    83.33%"
    difficulty: number
    stageId: string
}

export type ProjectPassageForApi = {
    entityId: string
    difficulty: number // e.g. 2.0 for a passage twice as hard as average passage
    passageName: string // e.g. 'Second Passage'
    taskId: string // e.g. '1.1'` (status of this passage)
    bars: string // " ◇ ◼ ◼ ◼ ◼ ◼|◻ ◇    83.33%"
    portionName: string // e.g. 'First Portion'
    scripture?: string, // "GEN 9:18-28, 10:1, 11:1 - 11:9; JHN 20:1-18"
    scriptureErrors?: RefRangeString, // e.g. space-separated list of invalid references or ranges '001100-001001100 002999' (Genesis 100 - 1:100; Exodus 999)
}

type CommaSeparatedListString = string /* comma separated list in string "a,b,c" */

Get project passages

This provides a list of a single project's passages (and their tasks)

HTTP Request

curl --request GET \
  --url https://api.sltt-bible.net/partner/projects/YourExampleProject1/passages \
  -u client--createdAt--1696539988381--partnerId--example.com:aeb433366bce021414c288bb9e5ff992

GET https://https://api.sltt-bible.net/partner/projects/{projectId}/passages -u {clientId}:{token}

NOTE: The cursor query parameter should be used with the GET /partner/projects/{projectsId}/passages endpoint to paginate through a project's passages if its response.summary.unprocessed.queryParams.cursor is present. (Likewise, if GET /batch/partner/passages returns its projectId in batchSummary.unprocessed.projectIdsWithCursors and its cursor is present in its batchResponse[].summary.unprocessed.queryParams.cursor)

ALSO IMPORTANT: The client will be responsible for merging the paginated passages data from the different requests. Also, the summary shows the passages/portions for the paginated data, not all the passages/portions for the project.

Response Example

{
    "summary": {
        "partnerId": "system",
        "projectId": "YourExampleProject1",
        "unprocessed": {
            "queryParams": {
                "cursor": null
            }
        },
        "processed": {
            "stagesCount": 4,
            "tasksCount": 8,
            "taskDifficulties": [
                " 0,1,1,1,1,1,1,0"
            ],
            "passageCounts": [
                "64,0,0,0,0,0,0,0"
            ],
            "passageCountsByPortion": [
                " 4,0,0,0,0,0,0,0 - KGH 05",
                " 4,0,0,0,0,0,0,0 - KGH 06",
                " 4,0,0,0,0,0,0,0 - KGH 07",
                " 4,0,0,0,0,0,0,0 - KGH 08",
                " 4,0,0,0,0,0,0,0 - KGH 09",
                " 4,0,0,0,0,0,0,0 - KGH 10",
                " 4,0,0,0,0,0,0,0 - KGH 11",
                " 4,0,0,0,0,0,0,0 - KGH 12",
                " 4,0,0,0,0,0,0,0 - KGH 13",
                " 4,0,0,0,0,0,0,0 - KGH 16",
                " 4,0,0,0,0,0,0,0 - KGH 17",
                " 4,0,0,0,0,0,0,0 - KGH 18",
                " 4,0,0,0,0,0,0,0 - KGH 19",
                " 4,0,0,0,0,0,0,0 - KGH 20",
                " 4,0,0,0,0,0,0,0 - KGH 22",
                " 4,0,0,0,0,0,0,0 - KGH 31 B"
            ],
            "portionsCount": 16,
            "passagesCount": 64,
            "passagesInProgressCount": 0,
            "bytes": 11998
        }
    },
    "response": {
        "project": {
            "name": "YourExampleProject1",
            "displayName": "Your Example Project 1",
            "description": "",
            "language": "",
            "progress": 0,
            "plannedCompletionDate": "",
            "lastUpdated": "2023/02/09 12:17:01.975Z",
            "lastVideoAt": "2023-12-13T08:38:16.351Z",
            "lastNoteAt": "2024-01-12T09:22:28.318Z",
            "activityStatus": "trained",
            "totalVideos": 5,
            "totalNotes": 48,
            "projectType": "translation",
            "region": "africa",
            "teamId": "YourTeamId"
        },
        "tasks": [
            {
                "taskId": "Not started",
                "taskName": "Not started",
                "bars": "|◇ ◻ ◻ ◻ ◻ ◻ ◻ ◇      0%",
                "difficulty": 0,
                "difficultyPercent": 0,
                "stageId": "Not started"
            },
            // ...
        ],
        "passages": [
            {
                "entityId": "230209_114806/230209_120913",
                "passageName": "01.Topic",
                "taskId": "Not started",
                "bars": "|◇ ◻ ◻ ◻ ◻ ◻ ◻ ◇      0%",
                "difficulty": 1,
                "portionName": "KGH 05",
                "scripture": "GEN 7:1-24"
            },
            //...
        ],
    },
}

Response Schema

export type GetPartnerProjectPassagesResponse = {
    summary: {
        partnerId: string,
        projectId: string,
        unprocessed: {
            queryParams: {
                getItemsLimit: number,
                cursor: string,
            }
        },
        processed: {
            stagesCount: number,
            tasksCount: number,
            taskDifficulties: string[],
            passageCounts: string[],
            passageCountsByPortion: string[],
            portionsCount: number,
            passagesCount: number,
            passagesInProgressCount: number,
            bytes: number,
        },
    },
    response: PartnerProjectPassagesResponse
}

type PartnerProjectPassagesResponse = {
    project: ProjectMetadataBasic,
    tasks: ProjectTaskForApi[],
    passages: ProjectPassageForApi[],
}

export type ProjectTaskForApi = {
    taskId: string
    taskName: string
    bars: string // " ◇ ◼ ◼ ◼ ◼ ◼|◻ ◇    83.33%"
    difficulty: number
    stageId: string
}

export type ProjectPassageForApi = {
    entityId: string
    difficulty: number // e.g. 2.0 for a passage twice as hard as average passage
    passageName: string // e.g. 'Second Passage'
    taskId: string // e.g. '1.1'` (status of this passage)
    bars: string // " ◇ ◼ ◼ ◼ ◼ ◼|◻ ◇    83.33%"
    portionName: string // e.g. 'First Portion'
    scripture?: string, // "GEN 9:18-28, 10:1, 11:1 - 11:9; JHN 20:1-18"
    scriptureErrors?: RefRangeString, // e.g. space-separated list of invalid references or ranges '001100-001001100 002999' (Genesis 100 - 1:100; Exodus 999)
}

type CommaSeparatedListString = string /* comma separated list in string "a,b,c" */

Get project events

This provides a list of a single project's (passage and video) events

HTTP Request

curl --request GET \
  --url https://api.sltt-bible.net/partner/projects/YourExampleProject1/events \
  -u client--createdAt--1696539988381--partnerId--example.com:aeb433366bce021414c288bb9e5ff992

GET https://https://api.sltt-bible.net/partner/projects/{projectId}/events -u {clientId}:{token}

NOTE: The cursor query parameter should be used with the GET /partner/projects/{projectsId}/events endpoint to paginate through a project's events if its response.summary.unprocessed.queryParams.cursor is present. For more information see the Appendix 1: Request Pagination section below.

NOTE: date may not be in sequential (eseq) order, due to the nature of events being made locally first (sometimes offline) and then later synced to the server.

NOTE: For older updated/removed/resolved/unresolved events (before Aug 2024), eventType will show creator along with the creator's emailHash, userRole and info. This is not necessarily the same user who actually performed the update. Newer updated events should instead show modifier and the correct emailHash, userRole, and info for who actually performed the update. (The dashboard detailed events export has the same issue.)

Response Example

{
        "summary": {
        "partnerId": "system",
        "projectId": "YourExampleProject1",
        "unprocessed": {
            "queryParams": {
                "cursor": "eyJwayI6IiRzbHR0LWFwaSNwcm9qZWN0aWRfc3N0cnMiLCJzayI6IiRldmVudHNfMSNlc2VxXzAwMDAwMDMyNTUifQ"
            }
        },
        "processed": {
            "eventsCount": 3255,
            "bytes": 1347702
        }
    },
    "response": {
        "events": [
            {
                "eseq": 1,
                "date": "2020/05/11 09:35",
                "oseq": 10,
                "event": "passage_created",
                "entity": "passage",
                "eventType": "created",
                "removed": false,
                "entityId": "200511_003121/200511_003536",
                "portionId": "200511_003121",
                "passageId": "200511_003536",
                "portionName": "FGH#56",
                "passageName": "Story#56",
                "difficulty": 1,
                "taskId": "Not started",
                "emailHash": "19088a8b1e69cb8f",
                "eventRole": "creator",
                "info": "creator: translator 19088a8b1e69cb8f",
                "userRole": "translator",
                "projectId": "YourExampleProject1"
            },
            {
                "eseq": 2,
                "date": "2020/05/11 09:36",
                "oseq": 11,
                "event": "video_post",
                "entity": "video",
                "eventType": "post",
                "removed": false,
                "entityId": "200511_003121/200511_003536/200511_003537",
                "portionId": "200511_003121",
                "passageId": "200511_003536",
                "portionName": "FGH#56",
                "passageName": "Story#56",
                "emailHash": "19088a8b1e69cb8f",
                "eventRole": "creator",
                "info": "creator: translator 19088a8b1e69cb8f",
                "userRole": "translator",
                "projectId": "YourExampleProject1"
            },
            {
                "eseq": 3,
                "date": "2020/05/11 09:37",
                "oseq": 12,
                "event": "passage_removed",
                "entity": "passage",
                "eventType": "removed",
                "removed": true,
                "entityId": "200511_003121/200511_003536",
                "portionId": "200511_003121",
                "passageId": "200511_003536",
                "portionName": "FGH#56",
                "passageName": "Story#56",
                "difficulty": 1,
                "taskId": "Not started",
                "emailHash": "70ddfbb6b68ae7ad",
                "eventRole": "modifier", // older events (before Aug 2024) show "creator" user instead of "modifier" user
                "info": "modifier: admin 70ddfbb6b68ae7ad",
                "userRole": "admin",
                "projectId": "YourExampleProject1"
            },
            // ...
        ]
    }
}

Response Schema


export type GetPartnerEventsResponse = {
    summary: {
        partnerId: string,
        projectId: string,
        unprocessed: {
            queryParams: {
                getItemsLimit: number,
                cursor: string,
            }
        },
        processed: {
            eventsCount: number,
            cursor: string,
            bytes: number,
        },
    },
    response: PartnerProjectEventResponse
}

type PartnerProjectEventResponse = {
    events: ProjectEventForApi[]
}

export type ProjectEventForApi = (PassageEventTypeForApi | VideoEventTypeForApi | NotYetSupportedDetailedEventForApi)

type PassageEventTypeForApi = {
    eseq: number
    date: string
    oseq: number,
    event: ProjectEventType
    entity: EventEntity.Passage
    eventType: BasicEventType
    removed: boolean
    entityId: string
    portionId: string
    passageId: string
    portionName: string
    passageName: string
    difficulty: number
    taskId: string
    taskName: string
    emailHash: string // 16 digit hash of user email
    eventRole: EventRole
    info: string
    userRole: ProjectMemberRole
    projectId: string
}

type VideoEventTypeForApi = {
    eseq: number
    date: string
    event: ProjectEventType
    entity: EventEntity.Video
    eventType: PostableEventType.Post
    removed: boolean
    entityId: string
    portionId: string
    passageId: string
    portionName: string
    passageName: string
    emailHash: string // 16 digit hash of user email
    eventRole: EventRole
    info: string
    userRole: ProjectMemberRole
    projectId: string
}

export enum EventEntity {
    Passage = 'passage',
    Video = 'video',
    // NOTE: the following "detailed events" are available via dashboard.sltt-bible.net Passage Status > Download details as TSV
    Task = 'task',
    Portion = 'portion',
    Note = 'note',
}

export enum ProjectEventType {
    passage_created = 'passage_created',
    passage_updated = 'passage_updated',
    passage_removed = 'passage_removed',
    video_post = 'video_post',
    // NOTE: the following "detailed events" are currently only available via dashboard.sltt-bible.net Passage Status > Download details as TSV)
    video_view = 'video_view', // detailed (via dashboard Passage Status > Download details as TSV)
    note_post = 'note_post',  // detailed (via dashboard Passage Status > Download details as TSV)
    note_view = 'note_view', // detailed (via dashboard Passage Status > Download details as TSV)
    note_resolved = 'note_resolved', // detailed (via dashboard Passage Status > Download details as TSV)
    note_unresolved = 'note_unresolved', // detailed (via dashboard Passage Status > Download details as TSV)
    portion_created = 'portion_created', // detailed (via dashboard Passage Status > Download details as TSV)
    portion_removed = 'portion_removed', // detailed (via dashboard Passage Status > Download details as TSV)
}

export enum EventEntity {
    Passage = 'passage',
    Video = 'video',
    // NOTE: the following "detailed events" are available via dashboard.sltt-bible.net Passage Status > Download details as TSV
    Task = 'task',
    Portion = 'portion',
    Note = 'note',
}

export enum BasicEventType {
    Created = 'created',
    Updated = 'updated',
    Removed = 'removed'
}

export enum PostableEventType {
    Post = 'post',
    // NOTE: the following "detailed events" are available via dashboard.sltt-bible.net Passage Status > Download details as TSV
    View = 'view'
}

export enum NoteEventType {
    // NOTE: the following "detailed events" are available via dashboard.sltt-bible.net Passage Status > Download details as TSV
    Resolved = 'resolved',
    Unresolved =  'unresolved'
}

enum EventRole {
    Creator = 'creator',
    Modifier = 'modifier',
    // NOTE: the following "detailed events" are available via dashboard.sltt-bible.net Passage Status > Download details as TSV
    Viewer = 'viewer'
}

enum ProjectMemberRole {
    admin = 'admin',
    translator = 'translator',
    consultant = 'consultant',
    interpreter = 'interpreter',
    observer = 'observer',
}

Appendix 1. Request Pagination

A 1.1 /partner endpoints

/partner/ endpoints that provide a summary object if there is a cursor query parameter in the summary.unprocessed.queryParams.cursor field. If so, then the client should pass the cursor as a query parameter for the next request.

For example,

{
    "summary": {
        "projectId": "Your-Example-Project",
        "partnerId": "your.example.partner-id",
        "unprocessed": {
            "count": 0,
            "queryParams": {
                "cursor": "eyJwayI6IiRzbHR0LWFwaSNwcm9qZWN0aWRfeW91ci1leGFtcGxlLXByb2plY3QiLCJzayI6IiRldmVudHNfMSNlc2VxXzAwMDAwMDMyNTUifQ=="
                //...
            }
        },
        //...
    }
}

To get the next page of results, pass the cursor as a query parameter for the next request:

curl --request GET \
  --url https://api.sltt-bible.net/partner/projects?cursor=eyJwayI6IiRzbHR0LWFwaSNwcm9qZWN0aWRfeW91ci1leGFtcGxlLXByb2plY3QiLCJzayI6IiRldmVudHNfMSNlc2VxXzAwMDAwMDMyNTUifQ== \
  -u client--createdAt--1696539988381--partnerId--example.com:aeb433366bce021414c288bb9e5ff992

A 1.2 /batch/ endpoints

/batch/ endpoints may require pagination of projectIds if results come from more projects than the batchGetKeyLimit specified (default is 100 projects). For as long as batchSummary.unprocessed.queryParams.unprocessedKeys is not empty in your last response, pass its value (a comma-delimited list of projectIds) as a query parameter for the next request.

For example, in the Response example below, batchSummary.unprocessed.queryParams.unprocessedKeys has __TESTnmX1__,__TESTnmX2__ for the request GET /batch/partner/projects?batchGetKeyLimit=1:

{
    "batchSummary": {
        "partnerId": "your.example.partner-id",
        "unprocessed": {
            "count": 2,
            "projectIds": ["__TESTnmX1__", "__TESTnmX2__"],
            "queryParams": {
                "batchGetKeyLimit": 100,
                "unprocessedKeys": "__TESTnmX1__,__TESTnmX2__",
            }
        },
        "processed": {
            "count": 1,
            "projectIds": [
                "YourExampleProject"
            ]
        },
    },
    //...
}

So the follow up request should look like

curl --request GET \
  --url https://api.sltt-bible.net/batch/partner/projects?unprocessedKeys=__TESTnmX1__,__TESTnmX2__ \
  -u client--createdAt--1696539988381--partnerId--example.com:aeb433366bce021414c288bb9e5ff992

Alternatively, if you think the request can complete within the timeout period (default is 10 seconds), you can pass a new batchGetKeyLimit that contains more than the projects you expect to get back (?batchGetKeyLimit=1000)

curl --request GET \
  --url https://api.sltt-bible.net/batch/partner/projects?batchGetKeyLimit=1000 \
  -u client--createdAt--1696539988381--partnerId--example.com:aeb433366bce021414c288bb9e5ff992

Appendix 2. Apply for an api token

A 2.1 Send an email

Example email:

To: epyle+sltt-client-api@biblesocieties.org

From: "Jon Doh"<jon.doh@example.com>

Subject: SLTT Client API Token Request

Hi, I'm Jon Doh from example.com. We would like to request a token for the SLTT Client API.

Here is the client information you requested:

  • Organization domain name: example.com

  • Contact name: Jon Doh

  • Contact email: jon.doh@example.com

  • Client description: example.com DOMO monthly SLTT usage report

Thank you so much for your help!

Jon

To apply for an api token, please contact the SLTT tech lead at epyle+sltt-client-api@biblesocieties.org.

You will need to email the following information for your client:

A 2.2 Receive clientId email

Within the next day or so, you should receive two separate emails. One with your clientId and another with your token to use for basic authentication.

Example reply:

------- reply --------

To: "Jon Doh"<jon.doh@example.com>

From: epyle+sltt-client-api@biblesocieties.org

Subject: Your SLTT Client API Token Request

Hi, Your SLTT Client api token application was granted. Here is your client profile including your clientId. Your token will be sent in a separate email.

{
    "partnerId": "example.com",
    "contactName": "Jon Doh",
    "contactEmail": "jon.doh@example.com",
    "clientDescription": "example.com DOMO monthly SLTT usage report",
    "clientId": "client--createdAt--1696539988381--partnerId--example.com",
}

Let me know if you need any further help!

Eric

A 2.3 Receive token email

Example reply:

------- reply --------

To: "Jon Doh"<jon.doh@example.com>

From: epyle+sltt-client-api@biblesocieties.org

Subject: follow-up email

Hi, Jon,

Here's the other information you will need:

{
    "token": "aeb433366bce021414c288bb9e5ff992",
}

Again, let me know if you need any further help!

Eric