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.
https://gateway.accessful.deAPI Version: 1.0.0
Content Types:
application/json, multipart/form-data
Key Features
- Batch Upload: Upload multiple PDF files simultaneously
- Asynchronous Processing: Webhook notifications for completion status
- Secure Authentication: OAuth2 with Keycloak integration
- HMAC Verification: Cryptographic verification of webhook callbacks
- Case Management: Unique UUID for each upload job
API Workflow
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.
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
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.
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.
API Endpoints
Upload PDF Files
POSTUpload 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
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
Upload PDF Files by URL
POSTUpload 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
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
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
GETRetrieve 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
Job Status Values
queued- Job is waiting to be processedrunning- Job is currently being processedcompleted- Job completed successfullyfailed- Job failed during processingcanceled- Job was canceled
Example Request
Example Response
{
"jobStatus": "completed",
"score": 95
}
Download Converted PDF
GETDownload 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
Delete Case
DELETEDelete 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
Example Request
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.
Webhook Request
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
- Authentication Service: Automatic JWT token management
- File Upload: Multi-file upload with webhook configuration
- Webhook Handler: Secure callback endpoint with HMAC verification
- Case Management: Optional cleanup and deletion of processed files
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/
Rate Limits & Best Practices
Rate Limits
- Upload requests may be limited per user account
- HTTP 429 response indicates rate limit exceeded
- Implement exponential backoff for retry logic
Best Practices
- Use Webhooks: More efficient than polling for job status
- Verify HMAC: Always validate webhook signatures
- Handle Duplicates: Check the duplicateFiles array in upload responses
- Clean Up: Delete cases when no longer needed to save storage
- Error Handling: Implement proper retry logic with exponential backoff
- Token Management: Cache and refresh tokens as needed
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;
}
}