Ally as a service

Authentication

Ally uses straight-forward Bearer token authentication with a signed JWT token (HS256). You will have been issued with a client Id and an application key/secret combination. Use these to generate and sign the JWT token. Then simply pass along a Authorization header with a Bearer token value.

The required parameters in the JWT payload are:

  • clientId - holds the numerical value of your provided and unique Ally client ID
  • iat - when the token was issued (seconds since epoch)

Example (javascript)
const jwt = require('jsonwebtoken');

const clientId = 'xxxx';
const applicationSecret = '...';
const payload = {
    'clientId': clientId
};
const token = jwt.sign(payload, applicationSecret);
const authorizationHeaderValue = `Bearer ${token}`;

You can make the rest of this page interactive, by setting your values here:

Example authorization header:


                              

Upload a file to Ally

Start the process by uploading the file you would like to get feedback for. This is a multipart POST request. The response will contain the statusUrl that you can poll to check whether the file has been processed. If the file was processed previously, a resourceUrl will be provided through which you can fetch the feedback information immediately.

Supported content types
  • PDFs
  • Office documents (.docx and .doc)
  • Office presentations (.pptx and .ppt)
  • Images
  • HTML files
Endpoint
POST /api/v2/clients/:clientId/content
Parameters
  • clientId - Path parameter - Your Ally Client ID
  • file - Post parameter - The binary file data
Expected response

There are two potential outcomes:

  1. The file is new and will be processed asynchronously. The endpoint will return with a 202 Accepted response and the following response body:
        {
            "hash": "UkVBRE1FLm1k...",
            "status": "pending",
            "statusUrl": "/api/v2/clients/:clientId/content/UkVBRE1FLm1k.../status,
            "resourceUrl": null
        }
    
  2. The file has been processed before. The endpoint will return a 303 See Other with a Location header to the feedback URL
cURL
curl \
  -H "Authorization: Bearer myJwtToken" \
  -F"file=@test.pdf" https://prod.ally.ac/api/v2/clients/:clientId/content
Try it out

Check the processing status of a file

Ally processes files asynchronously. We aim to process files in near-realtime, but some delays might be present. This endpoint can be used to poll for status updates.

Endpoint
GET /api/v2/clients/:clientId/content/:contentHash/status
Parameters
  • clientId - Path parameter - Your Ally Client ID
  • contentHash - Path parameter - The content hash from the previous endpoint
Expected response

This endpoint will always return a 200 status code for existing content hashes regardless of whether the file was processed successfully.

{
    "hash": "MS4gQWxsI...",
    "status": "success",
    "statusUrl": "/api/v2/clients/:clientId/content/MS4gQWxsIH.../status",
    "resourceUrl": "/api/v2/clients/:clientId/content/MS4gQWxsIH..."
}

The following status values can be identified:

  • success
  • pending
  • inprogress
  • failed
cURL
curl \
  -H "Authorization: Bearer myJwtToken" \
  https://prod.ally.ac/api/v2/clients/:clientId/content/:contentHash/status
Try it out

Retrieve the feedback for a file

Once the file is processed, the feedback can be retrieved

Endpoint
GET /api/v2/clients/:clientId/content/:contentHash
Parameters
  • clientId - Path parameter - Your Ally Client ID
  • contentHash - Path parameter - The content hash from the previous endpoint
  • feedback - Query parameter - Whether feedback information should be returned. Should be true, false or ommitted.
Expected response
    {
        "hash": "Mi4gSGVhZGluZ3NfdGFnZ2VkX2luY29ycmVjdGx5LnBkZjo4NkE3NEJGOTUxRDA2NEM4Qzk1ODUyMDI0NEQ0REJBNkM4RDdGRjJCOmFwcGxpY2F0aW9uL3BkZg==",
        "feedback": {
          "score": 0.4292113158749556,
          "visibility": "medium",
          "report": {
            "results": {
              "Scanned": {
                "score": 1
              },
              "Tagged": {
                "score": 1
              },
              "AlternativeText": {
                "snippets": [
                  {
                    "page": 3,
                    "y0": 397.45,
                    "x0": 72,
                    "y1": 720,
                    "x1": 526
                  },
                  {
                    "page": 4,
                    "y0": 394.81,
                    "x0": 72,
                    "y1": 720,
                    "x1": 476.8
                  },
                  ....
                ],
                "score": 0
              },
              "Contrast": {
                "snippets": [
                  {
                    "page": 0,
                    "y0": 588.6369,
                    "x0": 63.583138,
                    "y1": 612.7333,
                    "x1": 385.0053
                  },
                  {
                    "page": 1,
                    "y0": 688.4769,
                    "x0": 63.583138,
                    "y1": 712.5733,
                    "x1": 473.90308
                  },
                  ...
                ],
                "score": 0.9442992157651318
              },
              "HeadingsSequential": {
                "snippets": [
                  {
                    "page": 2,
                    "y0": 583.18,
                    "x0": 72.024,
                    "y1": 620.98,
                    "x1": 233.324
                  }
                ],
                "score": 0.9230769230769231
              },
              "HeadingsHigherLevel": {
                "snippets": [],
                "score": 1
              },
              "Title": {
                "score": 0
              },
              "TableHeaders": {
                "snippets": [
                  {
                    "page": 1,
                    "y0": 344.09,
                    "x0": 77.664,
                    "y1": 645.45996,
                    "x1": 517.17
                  }
                ],
                "score": 0
              },
              "LanguagePresence": {
                "score": 1
              },
              "HeadingsPresence": {
                "score": 1
              },
              "LanguageCorrect": {
                "detectedLanguage": "en",
                "score": 1
              },
              "HeadingsStartAtOne": {
                "snippets": [
                  {
                    "page": 0,
                    "y0": 586.06,
                    "x0": 72.024,
                    "y1": 620.26,
                    "x1": 376.75403
                  }
                ],
                "score": 0
              },
              "LibraryReference": {
                "score": 0
              }
            },
            "suggestions": {
              "HeadingsSequential": 0.4306552764930626,
              "Title": 0.4471530009744932,
              "TableHeaders": 0.47092573373138025,
              "HeadingsStartAtOne": 0.44798280391034667,
              "AlternativeText": 0.6169261962288668,
              "Contrast": 0.4515796153846155,
              "LibraryReference": 1
            }
          }
        },
        "formats": null,
        "metadata": {
          "name": "2. Headings_tagged_incorrectly.pdf",
          "decorative": null,
          "description": null,
          "fileType": "pdf",
          "mimeType": "application/pdf",
          "isVersioned": false,
          "isSeizureInducing": false,
          "libraryReference": null
        }
      }
cURL
curl \
  -H "Authorization: Bearer myJwtToken" \
  https://prod.ally.ac/api/v2/clients/:clientId/content/:contentHash
Try it out

Legal disclaimer

The Ally as a Service API page, along with its software and all content found on it (collectively, “AaaS”) are provided on an “as is” basis. There are no warranties, whether express or implied, as to the suitability, accuracy or usability of AaaS. You warrant that you are authorized to submit any content to AaaS and such submitted content will not contain any personal or confidential information. You, and not Blackboard, will be solely liable for any loss, whether such loss is direct, indirect, special or consequential, suffered by any party as a result of your use of AaaS.