DBL API

The DBL API provides programmatic access to the contents of the DBL. The current iteration (4.0) of the API allows for reading lists of entries, revisions, and revision contents, based on the identity and permissions of the client.

This page provides documentation of the API as a guide to implementers.

The API takes a restful approach; providing more specific terms in the request gives more specific information. The information scopes from a list of entries to the contents of a single revision of a single entry (under a specific license)

General Details

All queries to the API require the use of https. Queries made via http will be redirected with 303 response code.

All responses are by default in json format. Other formats (xml) can be forced with an extension type on the request, or by providing a Content-Type header. A provided extension will trump the provided header. Allowed values are JSON and XML.

Philosophy

The API provides methods for fetching and (someday) manipulating the various entities that exist within the DBL domain. For fetching information, there are terms that help specify the class of entity (user, organization, entry, etc.), terms that identify which of the entities are desired, and terms for identifying which attributes of the set of entities are required.

The API has been developed in a way that tries to combine these three aspects (what, which, attributes) into a single URI using a pattern that is composable and easily remembered.

There are competing approaches to URI design. Many web APIs will present a small set of URL endpoints and a large set of options in the form of URI arguments (i.e. /api?type=user&id=1). Our API is designed so that all of the specification (the which and attributes) are passed as URL path components (i.e. /api/users/1).

In a scheme where specification is controlled by URI arguments, the order of the arguments does not affect outcome. And so it is with the scheme used by this API. API that allows for multiple terms of specification are generally flexible in terms of the order of the terms. The one major exception to this rule is that if the entity identifiers are used as specifiers, then they appear immediately after the ‘what’. For instance, /api/users/1 can be read as /api/users/id/1 (which is not an accepted form). Other specifiers can be reordered, as in /api/license_agreements/publisher/545d2cb017c0caf3950136ae/copyright_holder/545d2cb007eaee5131ab123a (all license agreements with a publisher of id = 545d2cb017c0caf3950136ae and copyright holder = 2), which could be expressed with the same result as /api/license_agreements/copyright_holder/545d2cb007eaee5131ab123a/publisher/545d2cb017c0caf3950136ae. In both of these forms, the URI has the structure of /[what][/which]. In some cases, the specification term can be thought of as a yes/no value, in which case there may be hard-coded terms that take no argument, as in /api/license_agreements/expired, where the actual attribute is an expiry date that has passed or not. Special forms like this will be documented.

The URI can be completed with specifications of attributes. The very last term of the URI can be a comma-separated list of attributes. If the list is missing, then it is assumed that the client wants all attributes. One possible (not very useful) form would be /api/users/1/id, which would return a collection of the id attribute of the specified user. Since the id was already used to specify the user, this particular form is not helpful. On the other hand, /api/users/1/id,display_name will return a collection of one element that associates the id (1) with the name of the user that has that id.

Some attributes point to resources in their own right. A license agreement will have both a publisher and a copyright holder. Both of these are organizations, so the attribute value in the default case will be URIs that resolve to those organizations, not the organization detail. The API tries to avoid filling in details of entities in relationship with requested entities when there exists a canonical form of requesting that entity directly. The exception to this rule is when the URI has explicit terms requesting those attributes. In that case, the details of the sub-entity will be provided.

Recursion is explicitly disallowed, so the form /api/license_agreements/publisher/545d2cb017c0caf3950136ae/copyright_holder/full_name (get the full name of the copyright holders for all license agreements which have a publisher with id = 545d2cb017c0caf3950136ae) is not valid. Instead of copyright_holder being interpreted as a attribute specifier, it will be interpreted as an incomplete filter argument. full_name is interpreted as the attribute specification of the license agreement entity.

Authentication

The API provides two authentication methods. The older (deprecated) method involves providing an email an email address (the login name of the user making the API request) and a key as URI arguments. The second method uses special API keys, and has the requesing user embed the key plus a hash derived from the key and a shared secret in the headers of the request.

The old method has been deprecated for a couple of reasons. The first is that the authentication information provided as URI arguments is captured by logs and proxies, and could potentially captured for use in replay attacks. The second is that there is very little cryptographic strength in the provided key. The key is a simple hash of the user login and password, so knowing the key is the same as knowing the password. It is strongly discouraged to use the old authentication method.

Old Style Authentication

The old method of authentication requires that the requester provide two arguments to the URI, email and key. The value of email is the same value that the user would use for their login username. The value of key is a hash of a secret salt, the email string and the user’s password. The key value remains stable as long as the user doesn’t reset or change their password.

The current value of the key cannot be derived by the user, but can be requested with the form https://thedigitalbiblelibrary.org/api/user_token?email=<email>&password=<plaintext password>.

All currently supported API forms support authentication by this method. To use, append the arguments to the end of the URI (i.e. /api/users/1?email=<email>&key=<key>)

New Style Authentication

The new authentication method requires the API user to possess an API token and API secret key. Generation of the token/secret key pair is performed via the Digital Bible Library web site. An administrator of the organization that wants to allocate a new token/secret key needs to log in to the web site and navigate to the Admin|Organizations page for the organization they administer. The Roles tab contains a button labelled ‘Add New Access Token’ which looks like

../_images/access_token_button.png

Clicking on the button reveals a dialog which allows the administrator to set the permissions granted to the access token.

../_images/add_new_access_token_modal.png

Once the permissions are set and the administrator clicks Save, the UI presents both the access token and the secret key that is used to sign subsequent requests. This is the only opportunity to record the secret key.

../_images/secret_key.png

The access token and secret key are then used to sign API requests. The following code (in Python, using the requests module) demonstrates how to compose a string comprised of the http method, path, and some key headers. The string to sign is then inputted into a SHA1 hash engine along with the access token and secret key.

Both the client and server share the secret key info, so the server can recreate the signing information when the request is received. If the signatures match, then the server knows that the sender has access to the secret key and allows the request to proceed with the permissions given to the access token.

try:
        from requests.auth import AuthBase
except ImportError as ex:
        AuthBase = None  # declare AuthBase to silence pep8 warnings
        if __name__ != "__main__":
                raise Exception('cannot import requests module')


class DBLAuthV1(AuthBase):
        authorization_header = 'X-DBL-Authorization'

        def __init__(self, api_token, private_key):
                super(DBLAuthV1, self).__init__()
                self.api_token = api_token.lower()
                self.private_key = private_key.lower()

        def __call__(self, r):
                r.headers[self.authorization_header] = self.make_authorization_header(r)
                return r

        def make_authorization_header(self, request):
                import hmac
                import hashlib

                mac = hmac.new(self.api_token, None, hashlib.sha1)
                mac.update(self.signing_string_from_request(request))
                mac.update(self.private_key.lower())
                return 'version=v1,token=%s,signature=%s' %
                    (self.api_token, mac.hexdigest().lower())

        def signing_string_from_request(self, request):
                dbl_header_prefix = 'x-dbl-'
                signing_headers = ['content-type', 'date']

                method = request.method
                # use request uri, but not any of the arguments.
                path = request.path_url.split('?')[0]
                collected_headers = {}

                for key, value in request.headers.iteritems():
                        if key == self.authorization_header:
                                continue
                        k = key.lower()
                        if k in signing_headers or k.startswith(dbl_header_prefix):
                                collected_headers[k] = value.strip()

                # these keys get empty strings if they don't exist
                if 'content-type' not in collected_headers:
                        collected_headers['content-type'] = ''
                if 'date' not in collected_headers:
                        collected_headers['date'] = ''

                sorted_header_keys = sorted(collected_headers.keys())

                buf = "%s %s\n" % (method, path)
                for key in sorted_header_keys:
                        val = collected_headers[key]
                        if key.startswith(dbl_header_prefix):
                                buf += "%s:%s\n" % (key, val)
                        else:
                                buf += "%s\n" % val
                return buf


if __name__ == "__main__":
        import requests
        from datetime import datetime
        from wsgiref.handlers import format_date_time
        from time import mktime

        auth = DBLAuthV1('60AD09DFA8126695262F',
            '768FA670E21B6AE949891EE74FFC94A2BD16DFBFF4C074621101F99696065CC9C9C557788B6335C7')
        response = requests.get('http://localhost:8000/api/orgs', auth=auth,
            headers={'Date': format_date_time(mktime(datetime.now().timetuple())),
                     'Content-Type': 'application/json'})
        print response.status_code

Entries

Obtaining a Collection of Basic Entry Data

Entries can be listed in a short form. This listing of entry data contains only basic information, suitable for display to a human user, such as name and primary rights-holder.

More detailed information is not available via this method, instead use the owned entries or publishable forms.

Entries can be fetched as one of four groups, publicly visible, licensed for open access, managed, or publishable. The last two categories are dependent on context, specifically based on the access and permissions of the asker.

By default, these lists will contain all entries within the grouping. The list can be restricted in two ways, first by asking for only the latest revision of an entry (by default all revisions will be reported), and second by asking for only text or audio entries.

For each of these categories, the results contain the same information. This is:

  • id : the identifier for the entry
  • confidential : (true|false) is the entry sensitive (this will only be true for some users asking for managed entries)
  • obsolete : (true|false) is the entry obsolete
  • latest : (true|false) is this the latest revision of the entry
  • href : an URI that will give the full detail of the entry
  • type : one of text or audio
  • name : the common name of the entry, generally in the language of the entry
  • language_name : the name of the language
  • language_code : the ISO code for the language
  • country_name : the name of the country
  • rights_holder : the name of the principle managing agency
GET /api/public_entries_list

Returns a list of all entries visible to the requester, as if they were an anonymous user. This includes details on all entries which are both not-confidential and not-obsolete.

GET /api/open_access_entries_list

Returns a list of all the entries that have been licensed to allow for public, anonymous consumption. These entries are explicitely made available under an unrestricted license.

GET /api/owned_entries_list

Returns a list of entries where the rights-holder is an organization that the requester is a member of.

GET /api/publishable_entries_list

Returns a list of entries where the requester is a member of an organization that has a license to publish the entry.

Qualifying Terms All of the groupings described above can be customized by providing the following qualifications:

GET /latest

This will restrict the result set to records that are marked as the latest revision of an entry.

GET /type/[text|audio]

This will restrict the result set to entries that are of type text or audio.

These two qualifying terms can be combined, so e.g. /api/public_entries_list/latest/type/text is valid.

Obtaining a List of Entries with Detail

The simplest use of the API is to ask for the list of owned entries or publishable entries.

Owned Entries

/api/entries returns a JSON result of the latest revision of each entry that is owned by an organization that the acting user belongs to.

Example Request

GET /api/entries

Response

{
  "entries": [
    {
      "languageCode": "eng",
      "dateCompleted": "1992-10-01",
      "rightsHolderAbbreviation": "EIPC",
      "languageScript": "Latin",
      "countryCode": "US",
      "bundleProducer": "Paratext/7.4.100.47",
      "rightsHolder": "Example Bible Society",
      "nameCommonLocal": "DBL Test Version with DC Local",
      "obsolete": false,
      "languageLDMLId": "en",
      "comments": "Verifying DBL upload",
      "idParatext": "2880c78491b2f8ce8b3a4e0b2c2ef4610f4ffabe",
      "rightsHolderLocal": "Example Bible Society",
      "agencyETEN": "UBS",
      "nameAbbreviation": "DBLTD",
      "languageNumerals": "",
      "agencyPublishing": "Example Bible Society",
      "typeVersion": "1.2",
      "archivist": "Archivist Name",
      "id": "2880c78491b2f8ce",
      "rightsHolderURL": "http://www.dblipc.org/",
      "pubPromoVersionInfo": "Promotional information text here ... (HTML)",
      "scope": "Bible with Deuterocanon",
      "dateUpdated": "2013-11-21T19:42:28.791741",
      "nameAbbreviationLocal": "DBLTD",
      "entrytype": "text",
      "revision": "28",
      "dateArchived": "2012-03-12T17:51:32.7907868+00:00",
      "description": "English: DBL Test Version with DC Bible with Deuterocanon",
      "confidential": "false",
      "languageDialectCode": "",
      "validates": true,
      "pubPromoEmail": "",
      "countryName": "United States",
      "languageLevel": "Common",
      "idTMS": "800ffc90-61c7-4eee-9d2e-3b846d211d34",
      "languageName": "English",
      "copyrightStatement": "New Bible Translation (Second Edition)
        (c)1992 Example Bible Society. All rights reserved.",
      "obsoletedby": null,
      "nameCommon": "DBL Test Version with DC",
      "rightsHolderFacebook": "https://www.facebook.com/testbible",
      "idSIL": "",
      "agencyTranslation": "Example Bible Society",
      "translationType": "Revision",
      "languageScriptDirection": "LTR",
      "latest": true
    }
  ]
}

Publishable Entries

/api/publishable_entries returns a JSON result of the latest revision of each entry that is licensed to the Organization that the acting user belongs to (the acting user must have ‘publisher’ rights within that organization).

Example Request

GET /api/publishable_entries

Response

{
  "entries": [
    {
      "languageCode": "eng",
      "dateCompleted": "1992-10-01",
      "rightsHolderAbbreviation": "EIPC",
      "languageScript": "Latin",
      "countryCode": "US",
      "bundleProducer": "Paratext/7.4.100.47",
      "rightsHolder": "Example Bible Society",
      "nameCommonLocal": "DBL Test Version with DC Local",
      "obsolete": false,
      "languageLDMLId": "en",
      "comments": "Verifying DBL upload",
      "idParatext": "2880c78491b2f8ce8b3a4e0b2c2ef4610f4ffabe",
      "rightsHolderLocal": "Example Bible Society",
      "agencyETEN": "UBS",
      "nameAbbreviation": "DBLTD",
      "languageNumerals": "",
      "agencyPublishing": "Example Bible Society",
      "typeVersion": "1.2",
      "archivist": "Archivist Name",
      "id": "2880c78491b2f8ce",
      "rightsHolderURL": "http://www.dblipc.org/",
      "pubPromoVersionInfo": "Promotional information text here ... (HTML)",
      "scope": "Bible with Deuterocanon",
      "dateUpdated": "2013-11-21T19:42:28.791741",
      "nameAbbreviationLocal": "DBLTD",
      "entrytype": "text",
      "revision": "28",
      "dateArchived": "2012-03-12T17:51:32.7907868+00:00",
      "description": "English: DBL Test Version with DC Bible with Deuterocanon",
      "confidential": "false",
      "languageDialectCode": "",
      "validates": true,
      "pubPromoEmail": "",
      "countryName": "United States",
      "languageLevel": "Common",
      "idTMS": "800ffc90-61c7-4eee-9d2e-3b846d211d34",
      "languageName": "English",
      "copyrightStatement": "New Bible Translation (Second Edition)
        (c)1992 Example Bible Society. All rights reserved.",
      "obsoletedby": null,
      "nameCommon": "DBL Test Version with DC",
      "rightsHolderFacebook": "https://www.facebook.com/testbible",
      "idSIL": "",
      "agencyTranslation": "Example Bible Society",
      "translationType": "Revision",
      "languageScriptDirection": "LTR",
      "latest": true
    }
  ]
}

Details for a Single Entry

Once a list of entries is received, a client may then ask for the details of a single entry. This is done by giving the id of the entry as part of the request.

/api/entries/<entry_id> returns the details of a sample entry, including all revisions and all licenses that apply to the entry.

The list of licenses contains only those licenses available to the acting user as a licensee (i.e. only those that are usable by the acting user in a download of revision contents).

Example Request

GET /api/entries/<entry_id>

Response

{
   "licenses":[
      {
         "publisher":{
            "ipc":true,
            "description":"Example Library Card Holder",
            "parent":null,
            "director_selected_admin":null,
            "director_signup_token":null,
            "director_name":"Example LCH Director",
            "abbreviation":"ELCH",
            "director_email":"director@dblipc.org",
            "contact_email":"dbl@dblipc.org",
            "full_name":"Example Library Card Holder",
            "path":"1:",
            "id":"545d2cb0c5f145edd9b4c7aa",
            "lch":true,
            "director_signed_on":"2014-09-01 00:00:00",
            "name":"ELCH",
            "url":"http://www.dbllch.org",
            "services_agreement":null,
            "local_name":"",
            "public":true
         },
         "date_license_revoked":null,
         "date_license":"2012-12-05",
         "date_license_expiry":"2015-12-05",
         "copyright_holder":{
            "ipc":true,
            "description":"Example Bible Society",
            "parent":1,
            "director_selected_admin":null,
            "director_signup_token":null,
            "director_name":"Example IPC Director",
            "abbreviation":"EIPC",
            "director_email":"director@dblipc.org",
            "contact_email":"dbl@dblipc.org",
            "full_name":"Example Bible Society",
            "path":"1:2:",
            "id":"545d2cb007eaee5131ab123a",
            "lch":true,
            "director_signed_on":"2014-09-01 00:00:00",
            "name":"EIPC",
            "url":"http://www.dblipc.org",
            "services_agreement":null,
            "local_name":"",
            "public":true
         },
         "publisher_rights":{
            "allowFootnotes":false,
            "allowExtendedNotes":false,
            "allowCrossReferences":false,
            "allowIntroductions":false
         },
         "id":2180
      }
   ],
  "revisions": [
    {
      "languageCode": "eng",
      "dateCompleted": "1992-10-01",
      "rightsHolderAbbreviation": "EIPC",
      "languageScript": "Latin",
      "countryCode": "US",
      "bundleProducer": "Paratext/7.4.100.47",
      "rightsHolder": "Example Bible Society",
      "nameCommonLocal": "DBL Test Version with DC Local",
      "obsolete": false,
      "languageLDMLId": "en",
      "comments": "Verifying DBL upload",
      "idParatext": "2880c78491b2f8ce8b3a4e0b2c2ef4610f4ffabe",
      "rightsHolderLocal": "Example Bible Society",
      "agencyETEN": "UBS",
      "nameAbbreviation": "DBLTD",
      "languageNumerals": "",
      "agencyPublishing": "Example Bible Society",
      "typeVersion": "1.2",
      "archivist": "Archivist Name",
      "id": "2880c78491b2f8ce",
      "rightsHolderURL": "http://www.dblipc.org/",
      "pubPromoVersionInfo": "Promotional information text here ... (HTML)",
      "scope": "Bible with Deuterocanon",
      "dateUpdated": "2013-11-21T19:42:28.791741",
      "nameAbbreviationLocal": "DBLTD",
      "entrytype": "text",
      "revision": "28",
      "dateArchived": "2012-03-12T17:51:32.7907868+00:00",
      "description": "English: DBL Test Version with DC Bible with Deuterocanon",
      "confidential": "false",
      "languageDialectCode": "",
      "validates": true,
      "pubPromoEmail": "",
      "countryName": "United States",
      "languageLevel": "Common",
      "idTMS": "800ffc90-61c7-4eee-9d2e-3b846d211d34",
      "languageName": "English",
      "copyrightStatement": "New Bible Translation (Second Edition)
        (c)1992 Example Bible Society. All rights reserved.",
      "obsoletedby": null,
      "nameCommon": "DBL Test Version with DC",
      "rightsHolderFacebook": "https://www.facebook.com/tesebible",
      "idSIL": "",
      "agencyTranslation": "Example Bible Society",
      "translationType": "Revision",
      "languageScriptDirection": "LTR",
      "latest": true
    },
    {
      "revision": "27"
    },
    {
      "revision": "26"
    }
  ]
}

Details for a Single Revision

The request can be further refined by specifying the revision that the client is interested in.

/api/entries/<entry_id>/revision/1 returns the details of the first revision of the sample entry. As a convenience, one can also specify the latest revision of an entry by using the special case /api/entries/<entry_id>/revision/latest.

Asking for revision details will also return the list of licenses available to the acting user.

Example Request

GET /api/entries/<entry_id>/revision/latest

Response

{
   "licenses":[
      {
         "publisher":{
            "ipc":true,
            "description":"Example Library Card Holder",
            "parent":null,
            "director_selected_admin":null,
            "director_signup_token":null,
            "director_name":"Example LCH Director",
            "abbreviation":"ELCH",
            "director_email":"director@dblipc.org",
            "contact_email":"dbl@dblipc.org",
            "full_name":"Example Library Card Holder",
            "path":"1:",
            "id":"545d2cb0c5f145edd9b4c7aa",
            "lch":true,
            "director_signed_on":"2014-09-01 00:00:00",
            "name":"ELCH",
            "url":"http://www.dbllch.org",
            "services_agreement":null,
            "local_name":"",
            "public":true
         },
         "date_license_revoked":null,
         "date_license":"2012-12-05",
         "date_license_expiry":"2015-12-05",
         "copyright_holder":{
            "ipc":true,
            "description":"Example Bible Society",
            "parent":1,
            "director_selected_admin":null,
            "director_signup_token":null,
            "director_name":"Example IPC Director",
            "abbreviation":"EIPC",
            "director_email":"director@dblipc.org",
            "contact_email":"dbl@dblipc.org",
            "full_name":"Example Bible Society",
            "path":"1:2:",
            "id":"545d2cb007eaee5131ab123a",
            "lch":true,
            "director_signed_on":"2014-09-01 00:00:00",
            "name":"EIPC",
            "url":"http://www.dblipc.org",
            "services_agreement":null,
            "local_name":"",
            "public":true
         },
         "publisher_rights":{
            "allowFootnotes":false,
            "allowExtendedNotes":false,
            "allowCrossReferences":false,
            "allowIntroductions":false
         },
         "id":2180
      }
   ],
   "revision":{
      "languageCode": "eng",
      "dateCompleted": "1992-10-01",
      "rightsHolderAbbreviation": "EIPC",
      "languageScript": "Latin",
      "countryCode": "US",
      "bundleProducer": "Paratext/7.4.100.47",
      "rightsHolder": "Example Bible Society",
      "nameCommonLocal": "DBL Test Version with DC Local",
      "obsolete": false,
      "languageLDMLId": "en",
      "comments": "Verifying DBL upload",
      "idParatext": "2880c78491b2f8ce8b3a4e0b2c2ef4610f4ffabe",
      "rightsHolderLocal": "Example Bible Society",
      "agencyETEN": "UBS",
      "nameAbbreviation": "DBLTD",
      "languageNumerals": "",
      "agencyPublishing": "Example Bible Society",
      "typeVersion": "1.2",
      "archivist": "Archivist Name",
      "id": "2880c78491b2f8ce",
      "rightsHolderURL": "http://www.dblipc.org/",
      "pubPromoVersionInfo": "Promotional information text here ... (HTML)",
      "scope": "Bible with Deuterocanon",
      "dateUpdated": "2013-11-21T19:42:28.791741",
      "nameAbbreviationLocal": "DBLTD",
      "entrytype": "text",
      "revision": "28",
      "dateArchived": "2012-03-12T17:51:32.7907868+00:00",
      "description": "English: DBL Test Version with DC Bible with Deuterocanon",
      "confidential": "false",
      "languageDialectCode": "",
      "validates": true,
      "pubPromoEmail": "",
      "countryName": "United States",
      "languageLevel": "Common",
      "idTMS": "800ffc90-61c7-4eee-9d2e-3b846d211d34",
      "languageName": "English",
      "copyrightStatement": "New Bible Translation (Second Edition)
        (c)1992 Example Bible Society. All rights reserved.",
      "obsoletedby": null,
      "nameCommon": "DBL Test Version with DC",
      "rightsHolderFacebook": "https://www.facebook.com/tesebible",
      "idSIL": "",
      "agencyTranslation": "Example Bible Society",
      "translationType": "Revision",
      "languageScriptDirection": "LTR",
      "latest": true
   }
}

If a client is only interested in revision metadata, the form /api/entries/<entry_id>/revision/latest/metadata.xml returns only the metadata associated with the revision. Owner or licenser status is required to be allowed to access the metadata.xml content.

Example Request

GET /api/entries/<entry_id>/revision/latest/metadata.xml

Response

<?xml version="1.0" encoding="UTF-8"?>
<DBLMetadata id="2880c78491b2f8ce" revision="38" type="text" typeVersion="1.3">
  <identification>
    <name>DBL Test Version with DC</name>
    <nameLocal>DBL Test Version with DC Local</nameLocal>
    <abbreviation>DBLTDd234343</abbreviation>
    <abbreviationLocal>DBLTD</abbreviationLocal>
    <scope>Bible with Deuterocanon</scope>
    <description>English: DBL Test Version Bible with Deuterocanon</description>
    <dateCompleted>1992-10-01</dateCompleted>
    <systemId type="tms">800ffc90-61c7-4eee-9d2e-3b846d211d34</systemId>
    <systemId type="paratext">2880c78491b2f8ce8b3a4e0b2c2ef4610f4ffabe</systemId>
    <bundleProducer>Paratext/7.5.0.0</bundleProducer>
  </identification>
  <confidential>false</confidential>
  <agencies>
    <etenPartner>UBS</etenPartner>
    <creator>Example Bible Society</creator>
    <publisher>Example Bible Society</publisher>
    <contributor />
  </agencies>
  <language>
    <iso>eng</iso>
    <name>English</name>
    <ldml>en</ldml>
    <rod />
    <script>Latin</script>
    <scriptDirection>LTR</scriptDirection>
    <numerals />
  </language>
  <country>
    <iso>US</iso>
    <name>United States</name>
  </country>
  <type>
    <translationType>Revision</translationType>
    <audience>Common</audience>
  </type>
  <bookNames>
    <book code="GEN">
      <long>GENESIS</long>
      <short>Genesis</short>
      <abbr>Gn</abbr>
    </book>
    <book code="EXO">
      <long>EXODUS</long>
      <short>Exodus</short>
      <abbr>Ex</abbr>
    </book>
    <book code="LEV">
      <long>LEVITICUS</long>
      <short>Leviticus</short>
      <abbr>Lv</abbr>
    </book>
    ... etc...
  </bookNames>
  <contents>
    <bookList default="true" id="1">
      <name>DBL Test Version with DC</name>
      <nameLocal>DBL Test Version with DC Local</nameLocal>
      <abbreviation>DBLTDd234343</abbreviation>
      <abbreviationLocal>DBLTD</abbreviationLocal>
      <description>Catholic Interconfessional Bible</description>
      <descriptionLocal>Catholic Interconfessional Bible</descriptionLocal>
      <books>
        <book code="GEN" />
        <book code="EXO" />
        <book code="LEV" />
        ... etc...
      </books>
    </bookList>
    <bookList id="2">
      <name>DBL Test Version</name>
      <nameLocal>DBL Test Version Local</nameLocal>
      <abbreviation>DBLT</abbreviation>
      <abbreviationLocal>DBLT</abbreviationLocal>
      <description>Western Protestant Bible</description>
      <descriptionLocal>Western Protestant Bible</descriptionLocal>
      <books>
        <book code="GEN" />
        <book code="EXO" />
        <book code="LEV" />
        ... etc...
      </books>
    </bookList>
  </contents>
  <progress>
    <book code="GEN" stage="4" />
    <book code="EXO" stage="4" />
    <book code="LEV" stage="4" />
    ... etc ...
  </progress>
  <contact>
    <rightsHolder>Example Bible Society</rightsHolder>
    <rightsHolderLocal>Example Bible Society</rightsHolderLocal>
    <rightsHolderAbbreviation>EIPC</rightsHolderAbbreviation>
    <rightsHolderURL>http://www.dblipc.org/</rightsHolderURL>
    <rightsHolderFacebook>https://www.facebook.com/testbible</rightsHolderFacebook>
  </contact>
  <copyright>
    <statement contentType="xhtml">
      <p>New Bible Translation (Second Edition)
      (c)1992 Example Bible Society. All rights reserved.</p>
    </statement>
  </copyright>
  <promotion>
    <promoVersionInfo contentType="xhtml">
      <p>New Bible Translation with Deuterocanonicals/Apocrypha. Scripture taken from the
      New Bible Translation(r) (Second Edition). Copyright (c)1992 Example Bible Society.
      Used by permission.</p>
      <p>This is a Digital Bible Library demonstration bundle. It should NOT BE USED
      for any publication purposes</p>
    </promoVersionInfo>
    <promoEmail contentType="xhtml" />
  </promotion>
  <archiveStatus>
    <archivistName>Archivist Name</archivistName>
    <dateArchived>2012-03-12T17:51:32.7907868+00:00</dateArchived>
    <dateUpdated>2014-07-31T19:58:31.409150</dateUpdated>
    <comments>Test upload</comments>
  </archiveStatus>
  <format>text/xml</format>
</DBLMetadata>

Revision Contents

In order to obtain the listing of contents of a revision, including URLs from which the resources can be downloaded, you must provide a license identifier to indicate which license will be used to download the contents. For example:

GET /api/entries/<entry_id>/revision/latest/license/124

The license must be for an organization that the acting user belongs to, and for which they have ‘publisher’ rights.

If the acting user is a member of the organization that owns the entry, then the specified license can be ‘owner’. For example:

GET /api/entries/<entry_id>/revision/latest/license/owner

Providing the ‘owner’ license when not acting as an owner will result in a response with an HTTP status code of 401 (unauthorized).

Revision Contents as Zip

As a convenience to scripts that download in bulk, clients can also request revision contents as a zip archive bundle. For example:

GET /api/entries/acef2bc597c450a1/revision/latest/license/124.zip

will be interpreted as a request for the contents as a zip archive.

Organizations

Listing Organizations

Organization info can be retrieved using the form /api/orgs. The root form will return the superficial information about the org and links to more substantial information. The list of orgs returned by this query include all orgs visible to the requesting user. This is normally the user’s home organization and all sub-organizations, and any other organizations that are marked as public.

Example Request

GET /api/orgs

Response

{
  "orgs": [
    {
      "director_email": "director@dblipc.org",
      "ipc": true,
      "full_name": "Example Bible Society",
      "parent": "https://thedigitalbiblelibrary.org/api/orgs/545d2cb007eaee5131ab123a",
      "director_name": "Example IPC Director",
      "abbreviation": "TBS",
      "public": true,
      "href": "https://thedigitalbiblelibrary.org/api/orgs/545d2cb02f8195e72a026d5c",
      "contact_email": "dbl@dblipc.org",
      "contact_url": "http://www.dblipc.org",
      "members": "https://thedigitalbiblelibrary.org/api/orgs/545d2cb02f8195e72a026d5c/members",
      "local_name": "",
      "sub_orgs": "https://thedigitalbiblelibrary.org/api/orgs/545d2cb02f8195e72a026d5c/sub_orgs",
      "id": 1,
      "lch": false
    },
    {
      "director_email": "director@dbllch.org",
      "ipc": false,
      "full_name": "Example Library Card Holder",
      "parent": "https://thedigitalbiblelibrary.org/api/orgs/545d2cb007eaee5131ab123a",
      "director_name": "Example LCH Director",
      "abbreviation": "ELCH",
      "public": true,
      "href": "https://thedigitalbiblelibrary.org/api/orgs/54b5df8f5117ad330ec5aae6",
      "contact_email": "dbl@dbllch.org",
      "contact_url": "http://www.dbllch.org",
      "members": "https://thedigitalbiblelibrary.org/api/orgs/54b5df8f5117ad330ec5aae6/members",
      "local_name": "",
      "sub_orgs": "https://thedigitalbiblelibrary.org/api/orgs/54b5df8f5117ad330ec5aae6/sub_orgs",
      "id": 2,
      "lch": true
    },
  ]
}

Specific organizations can be retrieved by supplying the ids of the organizations in a comma separated list. /api/orgs/545d2cb003f5772898d5891d,545d2cb007eaee5131ab123a returns full details for the organizations with ids 545d2cb003f5772898d5891d and 545d2cb007eaee5131ab123a.

Organizations also have attributes that can be individually requested through the use of field qualifiers. These are further arguments in the URL that restrict the values returned. For instance, /api/orgs/545d2cb003f5772898d5891d/director_name,director_email will return only the director name and email address of the organization with id 545d2cb003f5772898d5891d.

Example Request

GET /api/orgs/545d2cb003f5772898d5891d/director_name,director_email

Response

{
   "orgs":[
      {
         "director_email":"director@dblipc.org",
         "director_name":"Example IPC Director"
      }
   ]
}

This composition can be combined with organization id compounding, as in /api/orgs/545d2cb003f5772898d5891d,545d2cb007eaee5131ab123a/director_name,director_email.

When organization structures are requested without field qualifiers, some of the available attributes of an org will only be returned as an URL that can be followed for more detail. This generally applies to any field which would naturally result in a collection of resources. For example, the list of organization members is naturally a list of user resources. If the org information is requested without field specifier, the value for members will be something like /api/orgs/545d2cb003f5772898d5891d/members. If the request is made with the ‘members’ field specifier (perhaps combined with other field specifiers), the members value will be a list of URLs that identify user resources.

Example Request

GET /api/orgs/545d2cb003f5772898d5891d/members

Response

{
   "orgs":[
      {
         "members":[
            "https://thedigitalbiblelibrary.org/api/users/2",
            "https://thedigitalbiblelibrary.org/api/users/29",
            "https://thedigitalbiblelibrary.org/api/users/30"
         ]
      }
   ]
}

Users

Listing Users

Users can be listed much the same as organizations. Users resources are relatively simple by comparison. Common forms are:

/api/users returns a collection of all users

Example Request

GET /api/users

Response

{
   "users":[
      {
         "home_org":"https://thedigitalbiblelibrary.org/api/orgs/545d2cb003f5772898d5891d",
         "href":"https://thedigitalbiblelibrary.org/api/users/1",
         "display_name":"Example IPC Archivist",
         "id":1,
         "email":"archivist@dblipc.org"
      },
      {
         "home_org":"https://thedigitalbiblelibrary.org/api/orgs/545d2cb007eaee5131ab123a",
         "href":"https://thedigitalbiblelibrary.org/api/users/2",
         "display_name":"Example LCH Downloader",
         "id":2,
         "email":"downloader@dbllch.org"
      }
   ]
}

/api/users/2 returns details for a specific user

Example Request

GET /api/users/2

Response

{
   "users":[
      {
         "home_org":"https://thedigitalbiblelibrary.org/api/orgs/545d2cb007eaee5131ab123a",
         "href":"https://thedigitalbiblelibrary.org/api/users/2",
         "display_name":"Example LCH Downloader",
         "id":2,
         "email":"downloader@dbllch.org"
      }
   ]
}

/api/users/2/display_name,email returns details for the specifed field qualifiers for a specific user.

Example Request

GET /api/users/2/display_name,email

Response

{
   "users":[
      {
         "display_name":"Example LCH Downloader",
         "email":"downloader@dbllch.org"
      }
   ]
}

License Agreements

Listing License Agreements

License Agreements are contracts that establish permission for one organization to use the intellectual property of another under a defined set of terms. License agreements are distinct entities from the Licenses that give them their structure. Most users of the API will be concerned with license agreements and not licenses.

License agreements are most simply retrieved by using the form /api/license_agreements. This basic form will return representations of all license agreements involving an organization (either rights holder or publisher) that the current user is a member of.

Example Request

GET /api/license_agreements

Response

{
   "license_agreements":[
      {
         "agreement_text":"https://thedigitalbiblelibrary.org/api/license_agreements/1607/agreement_text",
         "publisher":"https://thedigitalbiblelibrary.org/api/orgs/545d2cb017c0caf3950136ae",
         "revoked":"",
         "license":"https://thedigitalbiblelibrary.org/api/licenses/2",
         "expiry":"2014-06-22",
         "state":"accepted",
         "href":"https://thedigitalbiblelibrary.org/api/license_agreements/1607",
         "copyright_holder":"https://thedigitalbiblelibrary.org/api/orgs/545d2cb007eaee5131ab123a",
         "date":"2011-06-22",
         "entry":"https://thedigitalbiblelibrary.org/api/entries/7625ccaf24dd46dd",
         "id":1607
      },
      {
         "agreement_text":"https://thedigitalbiblelibrary.org/api/license_agreements/1608/agreement_text",
         "publisher":"https://thedigitalbiblelibrary.org/api/orgs/545d2cb017c0caf3950136ae",
         "revoked":"",
         "license":"https://thedigitalbiblelibrary.org/api/licenses/2",
         "expiry":"2014-06-22",
         "state":"accepted",
         "href":"https://thedigitalbiblelibrary.org/api/license_agreements/1608",
         "copyright_holder":"https://thedigitalbiblelibrary.org/api/orgs/545d2cb007eaee5131ab123a",
         "date":"2011-06-22",
         "entry":"https://thedigitalbiblelibrary.org/api/entries/4238a9bcf164423a",
         "id":1608
      },
      {
         "agreement_text":"https://thedigitalbiblelibrary.org/api/license_agreements/4617/agreement_text",
         "publisher":"https://thedigitalbiblelibrary.org/api/orgs/545d2cb017c0caf3950136ae",
         "revoked":"",
         "license":"https://thedigitalbiblelibrary.org/api/licenses/3",
         "expiry":"2017-09-02",
         "state":"pending",
         "href":"https://thedigitalbiblelibrary.org/api/license_agreements/4617",
         "copyright_holder":"https://thedigitalbiblelibrary.org/api/orgs/545d2cb007eaee5131ab123a",
         "date":"2014-09-02",
         "entry":"https://thedigitalbiblelibrary.org/api/entries/9ba89cc06f9d409f",
         "id":4617
      }
   ]
}

Qualifying terms include:

  • specifying the identifiers for a license agreement directly (e.g. /api/license_agreements/1000),
  • specifying the publisher explicitly (e.g. /api/license_agreements/publisher/545d2cb017c0caf3950136ae),
  • specifying the rights holder explicitly (e.g. /api/license_agreements/copyright_holder/545d2cb007eaee5131ab123a),
  • specifying the entry by uid (e.g. /api/license_agreements/entry/e555434ec4677f3b),
  • specifying agreements that are either expired or have been revoked (e.g. /api/license_agreements/expired, /api/license_agreements/revoked)

As with other API methods, terms are compounded. If multiple terms are provided, then the result will be the intersection of the sets that result in providing terms individually.

Getting and Setting License Agreement State

License agreements also have state that can be managed in a limited way through the API. Once a license agreement has been prepared by the rights holder, it can be either accepted or declined by the publisher. This state change can be performed through the API by POSTing the new state to a selection of license agreements.

In order to push the license agreement state, a selection of license agreements that are currently in the proper state (‘pending’) must be selected and a POST command sent.

For instance, if a user with publisher role who is a member of a publisher org (e.g. org identifier = ‘2’), wanted to bulk-accept all outstanding license agreements, the user would first make a selection of all license agreements indexed by that publisher using /api/license_agreements/publisher/545d2cb017c0caf3950136ae/id,state (providing attribute specifiers id and state restrict the amount of information returned, but are optional). The resulting set might look like this:

{
   "license_agreements":[
      {
         "state":"accepted",
         "id":10666
      },
      {
         "state":"pending",
         "id":10668
      }
   ]
}

The user may then iterate over the list of license agreements in the return set and extract the license agreement IDs for those license agreements that need to be updated. To accept the agreements compose a URL that posts the new state for the license agreements specified like this:

POST /api/license_agreements/10668?state=accepted

Licenses

Listing Licenses

Licenses are the entities that form the basis of license agreements, but they are managed separately. License agreement entities will include pointers to the license that was used to compose them, but a license agreements text should be retrieved from the license agreement entity itself, not the license. The license entity will include generic placeholder text, and won’t include the text that describes the service options that a completed license agreement contains.

Licenses can be retrieved in the general case with /api/licenses. This will list licenses that are visible to the requester based on org membership of the requester.

Licenses can also be retrieved by qualifying the request with license ID numbers (e.g. /api/licenses/1). Individual attributes can be requested, using the same pattern that applies to all other API endpoints.

Uploading Content

Some organisations produce content that is archived in the DBL. The actual work of submitting the content is left to users of the organization that are given the rights of ‘archivist’. The process of uploading makes use of a dedicated API described here.

Key Concepts for Uploading

The upload process allows for submitting an entry to the DBL. The entry can either be a completely new entry or a new revision of an existing entry.

To upload an entry, the caller of the API must submit an upload job. The job parcels the metadata and some job specific information together. The job is submitted as an XML document. The job specification is publicly available here.

The job specification includes client identifiers (one or more) and an optional uploading-agency identifier. The client identifiers are simple strings that allow for multiple processes to collaborate on the upload of a single entry. The uploading-agency identifier exists to disambiguate which agency the uploader is representing for the job (some archivists can have that right for multiple agencies).

The job specification also describes in detail the content of the job. The metadata for the entry already describes the entry as a collection of resources. The job specification can augment that by inserting <atom> elements in the metadata <resource> elements. The atom information describes the data that will be uploaded as part of the job.

Once a job has been submitted, it is available for processing or cancelling. Processing the job involves having one or more client uploaders ‘claim’ the atoms that were described in the job specification, then upload the data for that atom. When all of the atoms in the job have been uploaded, the job can be completed by sending the message to set it’s status to ‘complete’

A job can be cancelled at any time by it’s creator by setting it’s status to ‘canceled’

A job belongs (and is only visible to) the user who submitted it. A user may have multiple jobs open at any time. Only the creator of a job can affect it (upload data, change state, etc.).

Because of the visibility restrictions applied to jobs, only authenticated (i.e. signed) requests to the upload API are accepted. See the section on authentication for more information.

The Upload API

The most basic of forms is the request to get a list of all outstanding jobs (created but not yet completed). The basic form of the request is

GET /api/uploader/jobs
Request Headers:
 
  • X-DBL-Authorization – signing string for request
Status Codes:

The request must be signed in order to provide context for the response.

The response is a list of jobs and all of the clients declared for that job.

Creating a job is accomplished with

POST /api/uploader/jobs
Form Parameters:
 
  • job – a valid job specification
Request Headers:
 
  • X-DBL-Authorization – signing string for request
Status Codes:

Once the job is created, a client can query it’s state with

GET /api/uploader/jobs/(uid: job_identifer)
Parameters:
  • job_identifer – the job’s unique identifier
Query Parameters:
 
  • brief – if present, the response will be the short form.
  • client (string) – providing an optional client identifier will highlight the atoms claimed by that client in the response.
Request Headers:
 
  • X-DBL-Authorization – signing string for request
Status Codes:

The job can be affected (have it’s status changed) by sending

POST /api/uploader/jobs/(uid: job_identifer)
Parameters:
  • job_identifer – the job’s unique identifier
Query Parameters:
 
  • brief – if present, the response will be the short form.
  • client (string) – providing an optional client identifier will highlight the atoms claimed by that client in the response.
Form Parameters:
 
  • status – the new status for the job
Request Headers:
 
  • X-DBL-Authorization – signing string for request
Status Codes:

The job’s atoms can be queried with

GET /api/uploader/jobs/(uid: job_identifer)/(path: atom_path)
Parameters:
  • job_identifer – the job’s unique identifier
  • atom_path – the path uniquely identifying the atom within the job specification
Query Parameters:
 
  • client (string) – providing an optional client identifier will highlight the atoms claimed by that client in the response.
Request Headers:
 
  • X-DBL-Authorization – signing string for request
Status Codes:

There are two ways to affect atoms. The client can either change the status (i.e. claim the atom) or post data to the atom.

For changing the atom state

POST /api/uploader/jobs/(uid: job_identifer)/(path: atom_path)
Parameters:
  • job_identifer – the job’s unique identifier
  • atom_path – the path uniquely identifying the atom within the job specification
Form Parameters:
 
  • status – the new status for the atom
Query Parameters:
 
  • client (string) – a required client identifier to mark as the owner of a claimed atom, or to validate as owner for a completed atom.
Request Headers:
 
  • X-DBL-Authorization – signing string for request
Status Codes:
  • 200 OK – no error
  • 400 Bad Request – no job data or incorrectly formed data. May also indicate an illegal status change.
  • 403 Forbidden – request wasn’t signed, or signed improperly
  • 404 Not Found – no such job exists or no such atom exists

To upload data, a client calls the same POST API as to set the atom status. The upload data path is assumed when there is no form with a ‘status’ value. It is considered an error if there is no data payload

POST /api/uploader/jobs/(uid: job_identifer)/(path: atom_path)
Parameters:
  • job_identifer – the job’s unique identifier.
  • atom_path – the path uniquely identifying the atom within the job specification.
Form Parameters:
 
  • atom – binary data (the full contents of the atom) passed as an attached file.
Query Parameters:
 
  • client (string) – a required client identifier to mark as the owner of a claimed atom, or to validate as owner for a completed atom.
Request Headers:
 
  • X-DBL-Authorization – signing string for request.
Status Codes:
  • 200 OK – no error.
  • 400 Bad Request – no job data or incorrectly formed data. May also indicate an illegal status change.
  • 403 Forbidden – request wasn’t signed, or signed improperly.
  • 404 Not Found – no such job exists or no such atom exists.