Accessful API

PDF to PDF/UA Conversion Service

REST API OAuth2 Webhooks PDF/UA v1.0.4

Overview

The Accessful API provides a comprehensive solution for converting PDF documents to PDF/UA (Universal Accessibility) format. PDF/UA is an ISO standard that ensures PDF documents are accessible to users with disabilities.

Base URL: https://gateway.accessful.de
API Version: 1.0.0
Content Types: application/json, multipart/form-data

Key Features

API Workflow

1. Authenticate
โ†’
2. Upload PDF(s)
โ†’
3. Process
โ†’
4. Webhook/Poll
โ†’
5. Download

Authentication

The Accessful API uses OAuth2 with the Resource Owner Password Credentials Grant flow through Keycloak.

๐Ÿ”‘ Test Your Authentication

Enter your credentials below to get a JWT token for testing the API:

Note: If automatic authentication fails due to CORS restrictions, you'll receive manual instructions below.

POST
https://iam.accessful.de/realms/accessful-realm/protocol/openid-connect/token

Parameters

Parameter Type Required Description
grant_type string Yes Must be "password"
client_id string Yes Your client ID (e.g., "accessful-api")
username string Yes Your username
password string Yes Your password

Example Request

curl -X POST "https://iam.accessful.de/realms/accessful-realm/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password&client_id=accessful-api&username=your_username&password=your_password"

Response

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkJlYXJlciJ9...",
  "expires_in": 300,
  "refresh_token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer"
}

API Key Access

If you prefer not to exchange OAuth tokens, you can authenticate every request with a static API key.

Header: X-API-Key: <your_api_key>
Applies to: All REST requests documented on this page

Include the X-API-Key header alongside your request payload. No Bearer token is required when this header is present.

curl -X GET "https://gateway.accessful.de/api/v1/status" \ -H "X-API-Key: your_api_key"

API Endpoints

Upload PDF Files

POST
/api/v1/upload-service/pdf/upload

Upload multiple PDF files for conversion to PDF/UA format. Optionally configure webhook notifications.

Headers

Header Value
Authorization Bearer {access_token}
Content-Type multipart/form-data

Form Parameters

Parameter Type Required Description
files File[] Yes Array of PDF files to upload
webhookUrl string No Callback URL for processing notifications
secret string No* HMAC secret for webhook verification (*Required if webhookUrl is provided)
folder-name string No Optional folder name that groups the uploaded PDFs in the user interface

Response Codes

200 Upload successful
400 Invalid file type or format
429 Upload limit exceeded
500 Internal server error

Success Response (200)

{
  "successfulUploads": [
    "c8c956bb-19c3-451b-826f-5b3129eee4c1",
    "6848df1b-c6cf-4ae4-8ddd-c474cc103d4e"
  ],
  "duplicateFiles": [],
  "message": "Upload completed successfully. Uploaded 2 files. 0 duplicates found.",
  "callbackUrl": "https://your-server.com/callback"
}

Example Request

curl -X POST "https://gateway.accessful.de/api/v1/upload-service/pdf/upload" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -F "files=@document1.pdf" \ -F "files=@document2.pdf" \ -F "webhookUrl=https://your-server.com/callback" \ -F "secret=your-hmac-secret" \ -F "folder-name=Marketing"

Upload PDF Files by URL

POST
/api/v1/upload-service/pdf/upload-by-url-list

Upload PDF files by providing URLs instead of file uploads. The service will download the PDFs from the provided URLs and process them for conversion to PDF/UA format. The request is accepted asynchronously and queued for processing.

Headers

Header Value
Authorization Bearer {access_token}
Content-Type application/json

JSON Body Parameters

Parameter Type Required Description
files FileEntry[] Yes Array of file entries containing URLs and filenames
callbackUrl string No Callback URL for processing notifications
hmacSignature string No* HMAC secret for webhook verification (*Required if callbackUrl is provided)

FileEntry Object

Property Type Required Description
url string Yes Valid URL to download the PDF file from
filename string Yes Filename ending with .pdf (alphanumeric, underscore, hyphen allowed)

Response Codes

202 Upload accepted for processing
400 Invalid URL format or filename
429 Upload limit exceeded
500 Internal server error

Success Response (202)

{
  "accepted": [
    {
      "uri": "https://example.com/document1.pdf",
      "jobId": "c8c956bb-19c3-451b-826f-5b3129eee4c1"
    },
    {
      "uri": "https://example.com/document2.pdf",
      "jobId": "6848df1b-c6cf-4ae4-8ddd-c474cc103d4e"
    }
  ],
  "failures": {},
  "callbackResult": "Callback sent successfully"
}

Example Request

curl -X POST "https://gateway.accessful.de/api/v1/upload-service/pdf/upload-by-url-list" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "files": [ { "url": "https://example.com/document1.pdf", "filename": "document1.pdf" }, { "url": "https://example.com/document2.pdf", "filename": "document2.pdf" } ], "callbackUrl": "https://your-server.com/callback", "hmacSignature": "your-hmac-secret" }'

Generic Example JSON

{
  "files": [
    {
      "url": "https://example.com/sample-document.pdf",
      "filename": "sample-document.pdf"
    }
  ],
  "callbackUrl": "https://your-server.com/webhook/callback",
  "hmacSignature": "your-webhook-secret"
}

Get Job Status

GET
/api/v1/upload-service/job-status/{caseId}

Retrieve the current processing status of a PDF conversion job together with the latest accessibility score.

Path Parameters

Parameter Type Description
caseId UUID Unique identifier for the conversion job

Response Codes

200 Status retrieved successfully
400 Invalid case ID format
404 Case ID not found

Job Status Values

  • queued - Job is waiting to be processed
  • running - Job is currently being processed
  • completed - Job completed successfully
  • failed - Job failed during processing
  • canceled - Job was canceled

Example Request

curl -X GET "https://gateway.accessful.de/api/v1/upload-service/job-status/c8c956bb-19c3-451b-826f-5b3129eee4c1" \ -H "Authorization: Bearer $ACCESS_TOKEN"

Example Response

{
  "jobStatus": "completed",
  "score": 95
}

Download Converted PDF

GET
/api/v1/upload-service/download/{caseId}

Download the converted PDF/UA file for a completed job.

Path Parameters

Parameter Type Description
caseId UUID Unique identifier for the conversion job

Response

Returns the converted PDF file as binary data with Content-Type: application/pdf. The pdf-version response header contains the iteration number that was downloaded.

Example Request

curl -X GET "https://gateway.accessful.de/api/v1/upload-service/download/c8c956bb-19c3-451b-826f-5b3129eee4c1" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -o converted-document.pdf

Delete Case

DELETE
/api/v1/upload-service/delete/{caseId}

Delete a case and its associated files. Only the case owner can delete it.

Path Parameters

Parameter Type Description
caseId UUID Unique identifier for the case to delete

Response Codes

200 Case deleted successfully
400 Invalid UUID format
500 Server error

Example Request

curl -X DELETE "https://gateway.accessful.de/api/v1/upload-service/delete/c8c956bb-19c3-451b-826f-5b3129eee4c1" \ -H "Authorization: Bearer $ACCESS_TOKEN"

Webhooks

Webhooks provide real-time notifications when PDF processing is complete. When you specify a webhook URL during upload, Accessful will send a POST request to your endpoint with job completion details.

Security: All webhook requests are signed with HMAC-SHA256 using your provided secret. Always verify the signature before processing webhook data.

Webhook Request

POST
https://your-server.com/callback/{caseId}

Headers

Header Description
Content-Type application/json
X-Signature Base64-encoded HMAC-SHA256 signature

Payload

{
  "jobStatus": "completed",
  "caseId": "c8c956bb-19c3-451b-826f-5b3129eee4c1",
  "fileName": "document.pdf"
}

HMAC Verification

Verify webhook authenticity by computing the HMAC-SHA256 signature of the request body using your secret:

Java Example

public boolean verifyHmac(String payload, String signature, String secret) {
    try {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        byte[] rawHmac = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
        String expected = Base64.getEncoder().encodeToString(rawHmac);
        return MessageDigest.isEqual(expected.getBytes(), signature.getBytes());
    } catch (Exception e) {
        return false;
    }
}

Node.js Example

const crypto = require('crypto');

function verifyHmac(payload, signature, secret) {
    const hmac = crypto.createHmac('sha256', secret);
    hmac.update(payload);
    const expected = hmac.digest('base64');
    return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

Complete Examples

Quick Start with cURL

# 1. Get access token
TOKEN=$(curl -s -X POST "https://iam.accessful.de/realms/accessful-realm/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password&client_id=accessful-api&username=your_username&password=your_password" \
  | jq -r '.access_token')

# 2. Upload PDF file
RESPONSE=$(curl -s -X POST "https://gateway.accessful.de/api/v1/upload-service/pdf/upload" \
  -H "Authorization: Bearer $TOKEN" \
  -F "files=@document.pdf" \
  -F "webhookUrl=https://your-server.com/callback" \
  -F "secret=your-hmac-secret")

# 3. Extract case ID
CASE_ID=$(echo $RESPONSE | jq -r '.successfulUploads[0]')

# 4. Check status (optional if using webhooks)
curl -X GET "https://gateway.accessful.de/api/v1/upload-service/job-status/$CASE_ID" \
  -H "Authorization: Bearer $TOKEN"

# 5. Download converted file
curl -X GET "https://gateway.accessful.de/api/v1/upload-service/download/$CASE_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -o converted-document.pdf

JavaScript/Node.js Example

const FormData = require('form-data');
const fs = require('fs');
const axios = require('axios');

class AccessfulClient {
    constructor(baseUrl, tokenUrl, clientId, username, password) {
        this.baseUrl = baseUrl;
        this.tokenUrl = tokenUrl;
        this.clientId = clientId;
        this.username = username;
        this.password = password;
        this.accessToken = null;
    }

    async authenticate() {
        const params = new URLSearchParams();
        params.append('grant_type', 'password');
        params.append('client_id', this.clientId);
        params.append('username', this.username);
        params.append('password', this.password);

        const response = await axios.post(this.tokenUrl, params);
        this.accessToken = response.data.access_token;
        return this.accessToken;
    }

    async uploadPdf(filePath, webhookUrl = null, secret = null) {
        if (!this.accessToken) {
            await this.authenticate();
        }

        const form = new FormData();
        form.append('files', fs.createReadStream(filePath));
        
        if (webhookUrl) {
            form.append('webhookUrl', webhookUrl);
        }
        if (secret) {
            form.append('secret', secret);
        }

        const response = await axios.post(`${this.baseUrl}/api/v1/upload-service/pdf/upload`, form, {
            headers: {
                ...form.getHeaders(),
                'Authorization': `Bearer ${this.accessToken}`
            }
        });

        return response.data;
    }

    async getJobStatus(caseId) {
        const response = await axios.get(`${this.baseUrl}/api/v1/upload-service/job-status/${caseId}`, {
            headers: {
                'Authorization': `Bearer ${this.accessToken}`
            }
        });
        return response.data;
    }

    async downloadPdf(caseId, outputPath) {
        const response = await axios.get(`${this.baseUrl}/api/v1/upload-service/download/${caseId}`, {
            headers: {
                'Authorization': `Bearer ${this.accessToken}`
            },
            responseType: 'stream'
        });

        const writer = fs.createWriteStream(outputPath);
        response.data.pipe(writer);
        
        return new Promise((resolve, reject) => {
            writer.on('finish', resolve);
            writer.on('error', reject);
        });
    }
}

// Usage
const client = new AccessfulClient(
    'https://gateway.accessful.de',
    'https://iam.accessful.de/realms/accessful-realm/protocol/openid-connect/token',
    'accessful-api',
    'your_username',
    'your_password'
);

async function convertPdf() {
    try {
        // Upload PDF
        const uploadResponse = await client.uploadPdf('./document.pdf');
        const caseId = uploadResponse.successfulUploads[0];
        console.log('Upload successful, case ID:', caseId);

        // Poll for completion
        let status = 'queued';
        while (status !== 'completed' && status !== 'failed') {
            await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
            status = await client.getJobStatus(caseId);
            console.log('Status:', status);
        }

        if (status === 'completed') {
            // Download converted PDF
            await client.downloadPdf(caseId, './converted-document.pdf');
            console.log('Download complete!');
        }
    } catch (error) {
        console.error('Error:', error.message);
    }
}

convertPdf();

SDK & Demo Application

We provide a Spring Boot demo application that demonstrates complete integration with the Accessful API, including webhook handling and HMAC verification.

Demo Application Features

Key Components

// AuthService - Handles JWT token retrieval
@Service
public class AuthService {
    public String retrieveJwt() {
        // Keycloak token request implementation
    }
}

// AccessfulApiService - Main API interaction
@Service
public class AccessfulApiService {
    public void cleanup(UUID caseId) {
        // Optional cleanup of processed files
    }
}

// CallbackRestController - Webhook endpoint
@RestController
@RequestMapping("/api/callback")
public class CallbackRestController {
    @PostMapping("/{caseId}")
    public ResponseEntity<Void> receiveCallback(
            @PathVariable UUID caseId,
            @RequestHeader("X-Signature") String signature,
            @RequestBody String payloadJson) {
        // HMAC verification and processing
    }
}

Configuration

# application.properties
spring.application.name=accessful-api-demo
server.port=8866
accessful.api.base-url=https://gateway.accessful.de
accessful.keycloak.token-url=https://iam.accessful.de/realms/accessful-realm/protocol/openid-connect/token
pilot.account.name=your-username
pilot.account.password=your-password
pilot.account.client-id=accessful-api
callback.hmac.secret=your-hmac-secret
callback.url=https://your-server.com/api/callback/
Demo Application: The complete Spring Boot demo is available in this repository, showing production-ready patterns for API integration, error handling, and security best practices.

Rate Limits & Best Practices

Rate Limits

Best Practices

Error Handling

// Example error handling patterns
try {
    const response = await uploadPdf(file);
} catch (error) {
    if (error.response?.status === 429) {
        // Rate limited โ€“ implement backoff
        await delay(Math.pow(2, retryCount) * 1000);
        return retry();
    } else if (error.response?.status === 400) {
        // Invalid file format
        throw new Error('Invalid PDF file format');
    } else {
        // Other errors
        throw error;
    }
}