Skip to main content

Complete a Compliance Form

Complete a Compliance Form by submitting the required digital signature. This endpoint is used to finalize PCI SAQ-A (Self-Assessment Questionnaire A) forms that merchants must complete to maintain PCI DSS compliance.

Overview

Use this endpoint to:

  • Submit PCI SAQ-A signatures to complete compliance forms
  • Capture digital signatures with IP, timestamp, and user agent
  • Transition forms from INCOMPLETE to COMPLETE
  • Maintain compliance records for audit purposes
  • Generate signed documents for merchant records

Use Cases

1. Basic Form Completion

Complete a compliance form with required signature details:

async function completeComplianceForm(formId, signerDetails) {
const response = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
pci_saq_a: {
ip_address: signerDetails.ipAddress,
name: signerDetails.name,
signed_at: new Date().toISOString(),
title: signerDetails.title,
user_agent: signerDetails.userAgent
}
})
}
);

const completedForm = await response.json();

console.log('Compliance Form Completed:');
console.log('ID:', completedForm.id);
console.log('State:', completedForm.state); // Should be 'COMPLETE'
console.log('Signed by:', completedForm.pci_saq_a.name);
console.log('Signed at:', completedForm.pci_saq_a.signed_at);
console.log('Signed file:', completedForm.signed_file);

return completedForm;
}

2. Web Application Integration

Capture signature from a web form:

async function handleFormSubmission(formId, formData) {
// Get client details
const ipAddress = await fetch('https://api.ipify.org?format=json')
.then(res => res.json())
.then(data => data.ip);

const userAgent = navigator.userAgent;

// Validate form data
if (!formData.signerName || !formData.signerTitle) {
throw new Error('Signer name and title are required');
}

// Complete the compliance form
const response = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
pci_saq_a: {
ip_address: ipAddress,
name: formData.signerName,
signed_at: new Date().toISOString(),
title: formData.signerTitle,
user_agent: userAgent
}
})
}
);

if (!response.ok) {
const error = await response.json();
throw new Error(`Failed to complete form: ${error._embedded?.errors?.[0]?.message || 'Unknown error'}`);
}

const completedForm = await response.json();

// Store completion record
await db.complianceRecords.create({
form_id: completedForm.id,
merchant_id: completedForm.linked_to,
signer_name: formData.signerName,
signer_title: formData.signerTitle,
signed_at: completedForm.pci_saq_a.signed_at,
ip_address: ipAddress,
user_agent: userAgent,
signed_file_url: completedForm.signed_file
});

console.log('Compliance form completed and recorded');
return completedForm;
}

3. Merchant Portal Signature Capture

Complete form through merchant portal:

async function merchantSignCompliance(merchantId, formId, portalUser) {
// Verify merchant owns this form
const formResponse = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{
method: 'GET',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
}
}
);

const form = await formResponse.json();

if (form.linked_to !== merchantId) {
throw new Error('Form does not belong to this merchant');
}

if (form.state === 'COMPLETE') {
throw new Error('Form is already completed');
}

if (form.state === 'EXPIRED') {
throw new Error('Form has expired and cannot be completed');
}

// Complete the form
const response = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
pci_saq_a: {
ip_address: portalUser.ipAddress,
name: portalUser.fullName,
signed_at: new Date().toISOString(),
title: portalUser.jobTitle,
user_agent: portalUser.userAgent
}
})
}
);

const completedForm = await response.json();

// Send confirmation email
await sendEmail(portalUser.email, {
subject: 'PCI SAQ-A Form Completed',
template: 'compliance_completed',
data: {
merchant_name: portalUser.merchantName,
signer_name: portalUser.fullName,
signed_at: completedForm.pci_saq_a.signed_at,
valid_until: completedForm.valid_until,
signed_file_url: completedForm.signed_file
}
});

console.log('Merchant compliance form completed');
return completedForm;
}

4. Automated Signature from Backend

Complete form programmatically (e.g., after merchant review):

async function autoCompleteComplianceForm(formId, merchantData, adminUser) {
// Log the action
await db.auditLog.create({
action: 'COMPLETE_COMPLIANCE_FORM',
form_id: formId,
merchant_id: merchantData.id,
admin_user: adminUser.id,
timestamp: new Date()
});

const response = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
pci_saq_a: {
ip_address: merchantData.lastKnownIP,
name: merchantData.authorizedSignatory,
signed_at: new Date().toISOString(),
title: merchantData.signatoryTitle,
user_agent: 'Automated System Completion'
}
})
}
);

const completedForm = await response.json();

console.log('Form auto-completed:', {
form_id: completedForm.id,
merchant: merchantData.businessName,
signer: merchantData.authorizedSignatory
});

return completedForm;
}

5. Compliance Workflow with Validation

Complete form with pre-submission validation:

async function completeWithValidation(formId, signatureData) {
// Fetch current form state
const currentForm = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{
method: 'GET',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
}
}
).then(res => res.json());

// Validation checks
const validations = {
state_is_incomplete: currentForm.state === 'INCOMPLETE',
not_expired: new Date(currentForm.valid_until) > new Date(),
has_unsigned_file: !!currentForm.unsigned_file,
signer_name_valid: signatureData.name && signatureData.name.length >= 2,
signer_title_valid: signatureData.title && signatureData.title.length >= 2,
ip_address_valid: /^(\d{1,3}\.){3}\d{1,3}$/.test(signatureData.ipAddress)
};

const allValid = Object.values(validations).every(v => v);

if (!allValid) {
console.error('Validation failed:', validations);
throw new Error('Form validation failed: ' +
Object.entries(validations)
.filter(([_, valid]) => !valid)
.map(([check, _]) => check)
.join(', ')
);
}

// Complete the form
const response = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
pci_saq_a: {
ip_address: signatureData.ipAddress,
name: signatureData.name,
signed_at: new Date().toISOString(),
title: signatureData.title,
user_agent: signatureData.userAgent
}
})
}
);

if (!response.ok) {
throw new Error('Failed to complete compliance form');
}

const completedForm = await response.json();

console.log('Form completed successfully after validation');
return completedForm;
}

6. Bulk Form Completion

Complete multiple forms for a merchant:

async function bulkCompleteComplianceForms(merchantId, formIds, signerDetails) {
const results = {
successful: [],
failed: []
};

for (const formId of formIds) {
try {
const response = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
pci_saq_a: {
ip_address: signerDetails.ipAddress,
name: signerDetails.name,
signed_at: new Date().toISOString(),
title: signerDetails.title,
user_agent: signerDetails.userAgent
}
})
}
);

if (response.ok) {
const completedForm = await response.json();
results.successful.push({
form_id: formId,
signed_at: completedForm.pci_saq_a.signed_at
});
console.log(`✓ Completed form ${formId}`);
} else {
const error = await response.json();
results.failed.push({
form_id: formId,
error: error._embedded?.errors?.[0]?.message || 'Unknown error'
});
console.error(`✗ Failed form ${formId}`);
}

// Rate limiting
await new Promise(resolve => setTimeout(resolve, 500));

} catch (error) {
results.failed.push({
form_id: formId,
error: error.message
});
}
}

console.log(`Bulk completion complete: ${results.successful.length} successful, ${results.failed.length} failed`);
return results;
}

7. Webhook-Triggered Completion

Complete form when triggered by external event:

async function handleComplianceWebhook(webhookPayload) {
const { form_id, merchant_id, signature_data } = webhookPayload;

// Verify webhook authenticity
if (!verifyWebhookSignature(webhookPayload)) {
throw new Error('Invalid webhook signature');
}

// Complete the form
const response = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${form_id}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
pci_saq_a: {
ip_address: signature_data.ip_address,
name: signature_data.signer_name,
signed_at: signature_data.signed_at || new Date().toISOString(),
title: signature_data.signer_title,
user_agent: signature_data.user_agent
}
})
}
);

const completedForm = await response.json();

// Trigger downstream processes
await processComplianceCompletion(completedForm);

console.log('Webhook-triggered compliance completion:', completedForm.id);
return completedForm;
}

8. Complete with Document Generation

Complete form and generate custom certificate:

async function completeAndGenerateCertificate(formId, signatureData, merchantInfo) {
// Complete the compliance form
const response = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
pci_saq_a: {
ip_address: signatureData.ipAddress,
name: signatureData.name,
signed_at: new Date().toISOString(),
title: signatureData.title,
user_agent: signatureData.userAgent
}
})
}
);

const completedForm = await response.json();

// Generate compliance certificate
const certificate = await generateComplianceCertificate({
merchant_name: merchantInfo.businessName,
merchant_id: merchantInfo.id,
form_type: completedForm.type,
signer_name: signatureData.name,
signer_title: signatureData.title,
signed_at: completedForm.pci_saq_a.signed_at,
valid_from: completedForm.valid_from,
valid_until: completedForm.valid_until,
certificate_number: completedForm.id
});

// Store certificate
await db.certificates.create({
form_id: completedForm.id,
merchant_id: merchantInfo.id,
certificate_url: certificate.url,
issued_at: new Date()
});

console.log('Compliance form completed with certificate:', certificate.url);

return {
form: completedForm,
certificate: certificate
};
}

Request Parameters

Required Field: pci_saq_a

FieldTypeDescription
pci_saq_aobjectRequired - PCI SAQ-A signature details

pci_saq_a Object Fields

FieldTypeRequiredDescription
ip_addressstringYesIP address of the signer
namestringYesFull name of the person signing
signed_atstringYesISO 8601 timestamp of signature
titlestringYesJob title of the signer
user_agentstringYesBrowser/device user agent string

Response Fields

The response includes all compliance form fields plus the completed signature:

FieldTypeDescription
idstringCompliance form ID
statestringWill be COMPLETE after successful submission
typestringAlways PCI_SAQ_A
pci_saq_aobjectContains the signature details
signed_filestringURL to the signed PDF document
unsigned_filestringURL to the original unsigned PDF
valid_fromstringStart of validity period
valid_untilstringEnd of validity period
linked_tostringMerchant ID

Best Practices

1. Always Capture Real IP Address

Never use placeholder IPs:

// Good: Get actual client IP
const ipAddress = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress;

// Bad: Don't use placeholders
const ipAddress = '0.0.0.0'; // ❌ Never do this

2. Validate Before Submission

Check form state before attempting completion:

async function safeCompleteForm(formId, signatureData) {
// Fetch current state
const form = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{ /* ... */ }
).then(res => res.json());

// Validate state
if (form.state !== 'INCOMPLETE') {
throw new Error(`Cannot complete form in ${form.state} state`);
}

if (new Date(form.valid_until) < new Date()) {
throw new Error('Form has expired');
}

// Proceed with completion
return completeComplianceForm(formId, signatureData);
}

3. Use Accurate Timestamps

Always use the actual signing time:

pci_saq_a: {
// Good: Use current timestamp
signed_at: new Date().toISOString(),

// Bad: Don't use backdated times
// signed_at: '2020-01-01T00:00:00Z'
}

4. Store Signature Records

Maintain audit trail:

async function completeWithAudit(formId, signatureData, userId) {
const completedForm = await completeComplianceForm(formId, signatureData);

// Create audit record
await db.auditLog.create({
event_type: 'COMPLIANCE_FORM_COMPLETED',
form_id: completedForm.id,
merchant_id: completedForm.linked_to,
user_id: userId,
signature_details: signatureData,
completed_at: completedForm.pci_saq_a.signed_at,
ip_address: signatureData.ipAddress
});

return completedForm;
}

5. Handle Errors Gracefully

Provide clear error messages:

async function completeWithErrorHandling(formId, signatureData) {
try {
return await completeComplianceForm(formId, signatureData);
} catch (error) {
if (error.response?.status === 400) {
throw new Error('Invalid signature data. Please check all required fields.');
} else if (error.response?.status === 404) {
throw new Error('Compliance form not found.');
} else if (error.response?.status === 409) {
throw new Error('Form has already been completed.');
} else {
throw new Error('Failed to complete compliance form. Please try again.');
}
}
}

6. Download Signed Document

Retrieve the signed PDF after completion:

async function completeAndDownload(formId, signatureData) {
const completedForm = await completeComplianceForm(formId, signatureData);

// Download signed file
const signedPdfResponse = await fetch(completedForm.signed_file);
const signedPdfBlob = await signedPdfResponse.blob();

// Save to local storage or cloud
const filename = `compliance_${completedForm.id}_signed.pdf`;
await saveFile(filename, signedPdfBlob);

console.log('Signed document downloaded:', filename);

return {
form: completedForm,
local_file: filename
};
}

7. Notify Stakeholders

Send notifications after completion:

async function completeAndNotify(formId, signatureData, contacts) {
const completedForm = await completeComplianceForm(formId, signatureData);

// Notify merchant
await sendEmail(contacts.merchantEmail, {
subject: 'PCI Compliance Form Completed',
template: 'merchant_compliance_complete',
data: {
form_id: completedForm.id,
signer_name: signatureData.name,
signed_at: completedForm.pci_saq_a.signed_at,
valid_until: completedForm.valid_until,
signed_file: completedForm.signed_file
}
});

// Notify compliance team
await sendEmail(contacts.complianceTeam, {
subject: 'Merchant Completed PCI SAQ-A',
template: 'compliance_team_notification',
data: {
merchant_id: completedForm.linked_to,
form_id: completedForm.id,
completed_at: completedForm.pci_saq_a.signed_at
}
});

return completedForm;
}

Common Workflows

Standard Completion Flow

async function standardComplianceFlow(formId, merchantUser) {
// 1. Verify form is ready for signature
const form = await fetch(
`https://api.ahrvo.network/payments/na/compliance_forms/${formId}`,
{ /* ... */ }
).then(res => res.json());

if (form.state !== 'INCOMPLETE') {
throw new Error('Form is not ready for signature');
}

// 2. Capture signature details
const signatureData = {
ipAddress: merchantUser.ipAddress,
name: merchantUser.fullName,
title: merchantUser.jobTitle,
userAgent: merchantUser.userAgent
};

// 3. Complete the form
const completedForm = await completeComplianceForm(formId, signatureData);

// 4. Store completion record
await db.complianceHistory.create({
form_id: completedForm.id,
merchant_id: merchantUser.merchantId,
completed_at: completedForm.pci_saq_a.signed_at,
expires_at: completedForm.valid_until
});

// 5. Send confirmation
await sendEmail(merchantUser.email, {
subject: 'PCI Compliance Complete',
signed_document: completedForm.signed_file
});

return completedForm;
}

Interactive API Reference