ReShare Integration Guide

Overview and workflow

This document describes a suggested workflow for integrating external services with ReShare using ReShare APIs. Note that this approach is based on the current capabilities of the system, and this documentation will be updated as improved functionality is introduced.

A typical integration workflow will follow these steps:

  1. A user places an interlibrary loan request in an external system.

  2. The external system uses the ReShare Shared Inventory API to identify an eligible instance.

    1. Search by bibliographic metadata, such as ISBN, title, author.

    2. Identify a matching instance record.

    3. Confirm that this instance record has at least one holding with an ILL status of “Will lend.”

    4. Return the UUID of the Instance.

  3. The external system uses the ReShare OpenURL resolver to create a new request.

    1. The OpenURL must include the Instance UUID identified from the shared inventory.

    2. The OpenURL must include a patron ID of the same type that ReShare is using for verification with a particular library.

    3. The OpenURL resolver will return a confirmation of success and the UUID of the request in the library’s ReShare tenant.

  4. The external system uses the ReShare mod-rs API for each individual library tenant to poll for the status of the request using the UUID returned by the OpenURL resolver.

Required data

To complete the processes described in this document, you will need the following values. These values can be provided by the library and/or its service provider.

Shared Inventory API URL: This URL can be provided by your ReShare service provider. It will be the same for the whole consortium.

Shared Inventory username and password: Your service provider can set up a shared, read-only account that can be used for all libraries in the consortium.

Shared Inventory tenant: This tenant ID can be provided by your ReShare service provider. Note that when interacting with APIs, this ID must be lowercase.

OpenURL base URL: This URL can be provided by your ReShare service provider. It will be the same for the whole consortium.

Mod-rs API URL: This URL can be provided by your ReShare service provider. Each tenant will have its own URL.

Mod-rs username and password: Each library can create accounts in the Users app to support external integrations. The documented process for supporting the ILLiad add-on can serve as a good example of what’s needed. 

Mod-rs tenant: This tenant ID can be provided by your ReShare service provider. Note that when interacting with APIs, this ID must be lowercase.  Also note that this tenant ID is different than the “Shared Inventory Tenant” value above.

OpenURL tenant: This tenant ID can be provided by your ReShare service provider. Each library has a unique tenant ID, and it is typically equivalent to the ISIL symbol of the library (e.g., US-NJSOOS).

Patron ID type: This is not a value that is used specifically in any API calls; however, it is necessary to know what type of patron ID ReShare is using when constructing an OpenURL. ReShare uses the NCIP protocol to validate patrons on the fly rather than storing a patron database. Users can determine this by looking at the IDs used on records in their ReShare tenant and matching them to data that they have available in the external ILL service.

Pickup location mapping: Libraries must provide a mapping between all pickup locations used in the external ILL service and the slugs associated with each pickup location in ReShare. The external ILL service will need to use this mapping to identify the slug needed to set the “svc.pickupLocation” parameter in  the OpenURL.

Shared Inventory API

The shared inventory API allows searching by bibliographic data to identify records held by the consortium. The primary use cases for the API in the context of ReShare integration include identifying an eligible instance record for a request and capturing the UUID of that instance.

Additional documentation

Searching is done using GraphQL queries. Additional documentation on these queries can be found at: https://github.com/folio-org/mod-graphql/blob/master/doc/example-queries.md

Notes

The shared inventory harvests and aggregates data from many library catalogs using a match key algorithm. Duplicates are not uncommon, and it is possible a query based on bibliographic data may return more than one matching title in the shared inventory. In this situation, you may choose to either return the request for user review or develop a method for choosing a record from among the results, such as choosing the instance with the most available holdings.

Sample queries

This example uses curl to perform a title search in the shared inventory. Begin by obtaining a token for the shared index API following the instructions in the appendix.

Construct a query according to the instructions in the mod-graphql documentation linked above and save it in a file called “query.json”:

1 2 3 4 $ cat query.json  {   "query":"query {instance_storage_instances(query: \"title=boy on horseback\") { totalRecords  instances {  id title  }  }}" }

 

Set environment variables for the shared inventory tenant, url, and token:

1 2 3 $ export TENANT=shared_inventory_tenant $ export OKAPI_URL=https://shared_inventory_api_url.example.com $ export TOKEN=mytoken


Perform the graphql query:

1 2 3 4 5 6 $ curl  \    -H "X-Okapi-Tenant: $TENANT" \    -H "X-Okapi-Token: $TOKEN" \    -H "Content-Type: application/json" \    -H "Accept: application/json" \    -X POST  -d @query.json   $OKAPI_URL/graphql

 

Example response:

1 2 3 4 5 6 7 8 9 10 11 12 13 {      "data": {           "instance_storage_instances": {                "totalRecords": 1,                "instances": [                     {                          "id": "83b9a3a2-efc0-4cb4-977f-c6c7ca39f976",                          "title": "Boy on horseback"                     }                ]           }      } }

 

This example query will return the instance id and title of any matching instances. To check lendability status, perform an additional query on holdings using the id from the previous query as input.

1 2 3 4 5 6 7 8 9 10 $ cat holdings-query.json {   "query": "query { instance_storage_instances_SINGLE(instanceId: \"83b9a3a2-efc0-4cb4-977f-c6c7ca39f976\") { id title identifiers { value identifierTypeObject { id name } } holdingsRecords2 { id callNumber illPolicy { name }  permanentLocation { name code } holdingsStatements { note statement } bareHoldingsItems { id barcode enumeration } } } }" } $ curl  \    -H "X-Okapi-Tenant: $TENANT" \    -H "X-Okapi-Token: $TOKEN" \    -H "Content-Type: application/json" \    -H "Accept: application/json" \    -X POST  -d @holdings-query.json   $OKAPI_URL/graphql

 

Lending policy will be in the illPolicy.name key.

Example response:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 {      "data": {           "instance_storage_instances_SINGLE": {                "id": "83b9a3a2-efc0-4cb4-977f-c6c7ca39f976",                "title": "Boy on horseback",                "identifiers": [                     {                          "value": "9910496103569",                          "identifierTypeObject": {                               "id": "47a65482-f104-45e8-aead-1f12d70dcf32",                               "name": "RESHARE:US-EAST"                          }                     },                     {                          "value": "  35027284 ",                          "identifierTypeObject": {                               "id": "c858e4f2-2b6b-4385-842b-60732ee14abb",                               "name": "LCCN"                          }                     },                     {                          "value": "ocm00242026",                          "identifierTypeObject": {                               "id": "7e591197-f335-4afb-bc6d-a6d76ca3bace",                               "name": "System control number"                          }                     },                     {                          "value": "(PMilS)1049-millerdb-Voyager",                          "identifierTypeObject": {                               "id": "7e591197-f335-4afb-bc6d-a6d76ca3bace",                               "name": "System control number"                          }                     },                     {                          "value": "9910496103569",                          "identifierTypeObject": {                               "id": "6208e5fe-804d-4580-8b35-f912fd159a9e",                               "name": "RESHARE:US-SOUTH"                          }                     },                     {                          "value": "9910496103569",                          "identifierTypeObject": {                               "id": "9db07825-8035-4d9a-8a41-d59a5f1c337b",                               "name": "RESHARE:US-WEST"                          }                     },                     {                          "value": "9910496103569",                          "identifierTypeObject": {                               "id": "2fe92613-245e-4a4c-9559-e667738c33e7",                               "name": "RESHARE:US-NORTH"                          }                     }                ],                "holdingsRecords2": [                     {                          "id": "a7b24d62-d4bb-4a76-b3bf-e12b46d65def",                          "callNumber": "PN4874.S68 A35 1935",                          "illPolicy": {                               "name": "Will not lend"                          },                          "permanentLocation": {                               "name": "East",                               "code": "US-EAST/EC/EL/ELS"                          },                          "holdingsStatements": [],                          "bareHoldingsItems": [                               {                                    "id": "1c8b8d44-4a9c-4164-834f-e92d3ac2f12f",                                    "barcode": "33151001869462",                                    "enumeration": null                               }                          ]                     },                     {                          "id": "12394d47-cf07-42d6-8241-0e918ac94f19",                          "callNumber": "PN4874.S68 A35 1935",                          "illPolicy": {                               "name": "Will lend"                          },                          "permanentLocation": {                               "name": "South",                               "code": "US-SOUTH/SC/SL/SLS"                          },                          "holdingsStatements": [],                          "bareHoldingsItems": [                               {                                    "id": "48f66818-ef1f-4961-819c-897248fc79ce",                                    "barcode": "33151001869462",                                    "enumeration": null                               }                          ]                     },                     {                          "id": "f1169883-6c43-427e-9663-3cca811c9184",                          "callNumber": "PN4874.S68 A35 1935",                          "illPolicy": {                               "name": "Will not lend"                          },                          "permanentLocation": {                               "name": "West",                               "code": "US-WEST/WC/WL/WLS"                          },                          "holdingsStatements": [],                          "bareHoldingsItems": [                               {                                    "id": "f1e0cd5b-17dc-4cfe-b091-94c7942d7ea3",                                    "barcode": "33151001869462",                                    "enumeration": null                               }                          ]                     },                     {                          "id": "1c479cf4-614b-4557-8cf1-2f6ee65c58f6",                          "callNumber": "PN4874.S68 A35 1935",                          "illPolicy": {                               "name": "Will not lend"                          },                          "permanentLocation": {                               "name": "North",                               "code": "US-NORTH/NC/NL/NLS"                          },                          "holdingsStatements": [],                          "bareHoldingsItems": [                               {                                    "id": "b940928e-33c3-4fbf-a2ed-d6541df65b08",                                    "barcode": "33151001869462",                                    "enumeration": null                               }                          ]                     }                ]           }      } }

Note, it’s possible that you’ll receive the error curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1) on this call, if that is the case, force curl to use http1.1 by adding the --http1.1 option.

OpenURL resolver

ReShare uses an OpenURL resolver to initiate requests from outside the system. At the current time, the resolver only supports requests for known items and you must include the unique ID of a shared inventory record, which can be identified using the Shared Inventory API described above.

Additional documentation 

Full documentation for the OpenURL resolver can be found at: https://github.com/openlibraryenvironment/listener-openurl/blob/master/doc/openurls-for-reshare.md

Notes

Constructing a base URL

  • Each ReShare consortium will have its own base URL. 

  • This URL must be followed by the OpenURL Tenant ID of the institution to which the requester belongs. This will ensure the request is directed to the right tenant.

  • Example: https://listener-openurl.folio-dev.indexdata.com/US-EAST.

Required fields

  • Pickup location (svc.pickupLocation): Code must match the slug of a pickup location directory entry in the requesting library’s ReShare Directory.

  • Shared Inventory UUID (rft_id): You must include the UUID of a shared index record, which can be found using the shared index API as described above.

  • Patron ID (req_id): If you are using NCIP, this value must be the identifier (for instance, barcode) for a valid patron in your library’s local ILS. Otherwise, it can be any value.

Sample OpenURL

Line breaks are used in this sample to increase clarity.  This OpenURL contains these required values:

  • OpenURL Tenant: “US-EAST”

  • Pickup location: “linderman-test”

  • Shared Inventory UUID: “​​​​83b9a3a2-efc0-4cb4-977f-c6c7ca39f976”

  • Patron ID: “2505428762917384”

To aid in troubleshooting, use a meaningful Referrer Identifier Descriptor (rfr_id) in the URL such as (in order of preference): 1) the URL of a webpage from which the OpenURL request initiated; 2) an assigned ‘info:sid’ URI from the Namespace of Source Identifiers used in the NISO OpenURL Framework; or 3) the URL of a webpage describing the system initiating the OpenURL request.

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 https://listener-openurl.folio-dev.indexdata.com/US-EAST? svc.pickupLocation=linderman-test& svc.neededBy=2021-09-17& rft.volume=1& svc.note=Test& ctx_enc=info%3Aofi%2Fenc%3AUTF-8& ctx_ver=Z39.88-2004& req_id=2505428762917384& rfr_id=...meaningful-referrer-identifier-descriptor…& rft.au=Steffens%2C+Lincoln%2C+1866-1936& rft.creator=Steffens%2C+Lincoln%2C+1866-1936& rft.date=%5B%C2%A91935%5D& rft.format=UnknownFormat& rft.pub=Harcourt%2C+Brace%2C& rft.title=Boy+on+horseback& rft_id=83b9a3a2-efc0-4cb4-977f-c6c7ca39f976& rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Adc& svc.ntries=1& url_ver=Z39.88-2004


mod-rs API

Mod-rs is ReShare’s resource sharing module. The mod-rs API can be used to return the current state for a given request. 

Notes

Each request in ReShare has two types of identifiers.

  • A UUID is an internal identifier that uniquely identifies the request in a particular institution’s ReShare tenant. The same request will have two different UUIDs -- one internal to the requester’s tenant and one internal to the supplier's tenant. You must use this ID to retrieve data about the request using  the Requester’s and Supplier’s mod-rs API endpoint.  The requester’s UUID and the supplier’s UUID linked by the HRID (see below).

  • A requests also has a human-readable ID (HRID). This ID uses a prefix identifying the Requester’s institution, plus a number that is incremented by one for each new request. The HRID is shared between the requester and supplier. The HRID can be used to search for the request UUID.

Each request will be assigned a state at any given time. A full list of possible states can be found at https://openlibraryenvironment.atlassian.net/wiki/spaces/PR/pages/759365633 .

For the purposes of integration, the states of greatest interest are those states that help determine the outcome of the request. These include:

  • Expects to supply: A supplier has been assigned and plans to provide the item

  • Shipped: The supplier has shipped the item to the requester.

  • Return shipped: The requester has returned the item to the supplier.

  • Complete: The supplier has received the returned item and closed the request.

  • End of rota: No supplier could be identified and the request cannot be filled.

  • Cancelled: The requester has cancelled the request.

Sample query

Begin by getting an API token following the instructions in the appendix.

 

Set environment variables using the mod-rs username, password, and api url

1 2 3 $ export TENANT=mod-rs_tenant $ export OKAPI_URL=https://mod-rs_api_url.example.com $ export TOKEN=mytoken


Get the status of a request from mod-rs using the request UUID

1 2 3 4 $ curl \   -H "X-Okapi-Tenant: $TENANT" \   -H "X-Okapi-Token: $TOKEN" \   $OKAPI_URL/rs/patronrequests/b4f9c7fc-5fdb-49eb-bf9b-1faab4d3a5bd 

Appendix: Getting an API token

Both the Shared Inventory and mod-rs run on the Okapi platform and share a common method of logging in. Note that the Shared Inventory and the mod-rs tenant of a specific library have distinct “X-Okapi-Tenant” values. The following example shows how to obtain an API token using cURL.

Set up environment variables for username, password, tenant, and Okapi URL These could be the values of the shared inventory url, tenant, username and password, and tenant for example.

1 2 3 4 $ export USER=myuser $ export PASSWORD=mypassword $ export TENANT=mytenant $ export OKAPI_URL=https://myokapi.example.com

 

Post your credentials to the login endpoint. This should return a 201 success code and a value for x-okapi-token in the headers. The x-okapi-token will be your API token. In this example, it’s saved toa file located at /tmp/loginheaders

1 2 3 4 5 $ curl -D /tmp/loginheaders -w '\n' -X POST  \   -H "Content-type: application/json" \   -H "X-Okapi-Tenant:$TENANT" \   -d "{\"username\":\"${SI_USER}\",\"password\":\"${SI_PASSWORD}\"}" \    $OKAPI_URL/authn/login

 

Set the returned x-okapi-token to the TOKEN environment variable

1 2 3 $ export TOKEN=$(cat /tmp/loginheaders | \   grep -i x-okapi-token | \   sed 's/^.*: //g')