Trillboards
shared.backToDevelopers
shared.developerDocs

proofOfPlay.heroTitleproofOfPlay.heroTitleAccent

proofOfPlay.heroSubtitle

proofOfPlay.howItWorksTitle

proofOfPlay.howItWorksDesc

Step 1

proofOfPlay.steps.step1.title

proofOfPlay.steps.step1.desc

Step 2

proofOfPlay.steps.step2.title

proofOfPlay.steps.step2.desc

Step 3

proofOfPlay.steps.step3.title

proofOfPlay.steps.step3.desc

proofOfPlay.modelTitle

proofOfPlay.modelDesc

βœ“

proofOfPlay.openVerification

proofOfPlay.openVerificationDesc

proofOfPlay.closedDiscovery

proofOfPlay.closedDiscoveryDesc

proofOfPlay.signatureTitle

proofOfPlay.signatureDesc

Signature payload format
v2.<timestamp>.<adId>.<impressionId>.<screenId>.<deviceId>
FieldTypeDescription
versionstringproofOfPlay.signatureFields.version
timestampnumberproofOfPlay.signatureFields.timestamp
adIdstringproofOfPlay.signatureFields.adId
impressionIdstringproofOfPlay.signatureFields.impressionId
screenIdstringproofOfPlay.signatureFields.screenId
deviceIdstringproofOfPlay.signatureFields.deviceId

proofOfPlay.signatureSuffix

proofOfPlay.verificationTitle

proofOfPlay.nodeJs

verify-proof.js
const crypto = require('crypto');

// Fetch from /v1/advertiser/proof/.well-known/public-key
const PUBLIC_KEY_HEX = 'a5a9c43785680c62ebcd01ec49a0d6055fd140023df0f142c3e37c6abafce34f';

function verifyProof(proof) {
  const payload = `v2.${proof.timestamp}.${proof.adId}.${proof.impressionId}.${proof.screenId}.${proof.deviceId}`;

  const signatureHex = proof.signature.replace('ed25519=', '');
  const signatureBytes = Buffer.from(signatureHex, 'hex');

  const publicKey = crypto.createPublicKey({
    key: Buffer.concat([
      Buffer.from('302a300506032b6570032100', 'hex'),
      Buffer.from(PUBLIC_KEY_HEX, 'hex')
    ]),
    format: 'der',
    type: 'spki'
  });

  return crypto.verify(null, Buffer.from(payload), publicKey, signatureBytes);
}

const proof = {
  impressionId: '507f1f77bcf86cd799439011',
  adId: 'campaign-abc123',
  screenId: 'screen-xyz789',
  deviceId: 'device-456',
  timestamp: 1705401600000,
  signature: 'ed25519=a1b2c3d4e5f6...'
};

console.log('Valid:', verifyProof(proof));

proofOfPlay.python

verify_proof.py
from nacl.signing import VerifyKey
from nacl.exceptions import BadSignature

PUBLIC_KEY_HEX = 'a5a9c43785680c62ebcd01ec49a0d6055fd140023df0f142c3e37c6abafce34f'

def verify_proof(proof):
    verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY_HEX))

    payload = f"v2.{proof['timestamp']}.{proof['adId']}.{proof['impressionId']}.{proof['screenId']}.{proof['deviceId']}"

    signature_hex = proof['signature'].replace('ed25519=', '')
    signature_bytes = bytes.fromhex(signature_hex)

    try:
        verify_key.verify(payload.encode(), signature_bytes)
        return True
    except BadSignature:
        return False

proofOfPlay.curl

terminal
# Verify a proof using the API (no auth required)
curl -X POST https://api.trillboards.com/v1/advertiser/proof/verify-proof \
  -H "Content-Type: application/json" \
  -d '{
    "signature": "ed25519=a1b2c3d4e5f6...",
    "timestamp": 1705401600000,
    "adId": "campaign-abc123",
    "impressionId": "507f1f77bcf86cd799439011",
    "screenId": "screen-xyz789",
    "deviceId": "device-456"
  }'

# Response:
# {
#   "success": true,
#   "verification": {
#     "valid": true,
#     "version": "v2"
#   }
# }

proofOfPlay.endpointsTitle

MethodPathAuthDescription
GET/v1/advertiser/proof/.well-known/public-key
POST/v1/advertiser/proof/verify-proof
GET/v1/advertiser/proof/proofs
GET/v1/advertiser/proof/proofs/:id

proofOfPlay.whyTitle

proofOfPlay.whyCards.tamperProof.title

proofOfPlay.whyCards.tamperProof.desc

proofOfPlay.whyCards.openVerification.title

proofOfPlay.whyCards.openVerification.desc

proofOfPlay.whyCards.closedDiscovery.title

proofOfPlay.whyCards.closedDiscovery.desc

proofOfPlay.whyCards.uniquePerImpression.title

proofOfPlay.whyCards.uniquePerImpression.desc