Technical integration guide
Everything needed to wire Verified Draws into a site: connect the plugin with a site token, push the final roster, generate provably-fair ticket numbers per order, pull results back through the endpoint contract, and render the stored verify links in your own theme.
Download the plugin zip from the /api/plugin/download endpoint, then upload it under Plugins → Add New → Upload in WordPress. Open Verified Draws → Settings and run the connection self-test.
The site key (site token) is generated locally on first use. It is sent only server-to-server over HTTPS as a Bearer token — never shown in a browser or placed in a URL. Each competition is registered once against POST /api/plugin/competition, which is idempotent per (site, competitionKey).
The final entrant roster is pushed to POST /api/ingest, authenticated with the Bearer site token. It sends only ticket values — plus entrant names if the operator opts in per push. It never sends payment details, addresses, or emails.
Once a competition is registered and its seed is ready — a committed future drand round publishes — the plugin assigns ticket numbers per order using the seed minted by POST /api/plugin/competition. Tickets are positions in a drand-seeded keyed permutation over the number range: deterministic, tamper-evident, and unpredictable while selling. Assignment is keyed on the order id (get-or-create / idempotent).
$assignment = vd_assign_tickets(
$competition_id, // WP post id of the registered competition
$order_id, // the buyer's order — the idempotency key
$count // how many tickets to assign
);
$assignment->tickets; // e.g. ["A001-UK", "A002-UK"]
$assignment->start_position; // first block index for this order
$assignment->verify_url; // /verify/order/<code>-<start>-<count>The per-order verify link follows the format https://verifieddraws.uk/verify/order/<code>-<start>-<count>.
https://verifieddraws.uk/verify/order/VCODE-7Q2F-0-3The endpoint is POST /api/plugin/results, authenticated with the Bearer site token. The request body takes three forms:
{ "sourceId": "adapter:ref" }— a single competition. Returns{ competition, draws }(404 if not found or not owned by this install).{ "sourceIds": ["...","..."] }— a batch over an explicit list. Returns{ competitions: [ { competition, draws }, ... ] }.- empty / absent body
{}— a batch over all of this install's competitions. Returns{ competitions: [...] }. This is what the plugin's one-click "Pull verification results" button uses.
curl -X POST https://verifieddraws.uk/api/plugin/results \
-H "Authorization: Bearer $VD_SITE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"sourceId":"woo:1234"}'curl -X POST https://verifieddraws.uk/api/plugin/results \
-H "Authorization: Bearer $VD_SITE_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'{
"competition": { "name": "Summer Cash Draw", "sourceId": "woo:1234" },
"draws": [
{
"rollIndex": 0,
"winner": { "number": 42, "ticket": "A042-UK" },
"winnerName": "Alex P.",
"publicCode": "VCODE-7Q2F",
"verifyUrl": "https://verifieddraws.uk/verify/VCODE-7Q2F",
"verified": true,
"revealedAt": "2026-06-13T20:14:00.000Z",
"skipped": [ { "number": 7, "ticket": "A007-UK" } ]
}
]
}winnerName is optional — present only for roughly an hour after reveal, while the display name is retained. In the API response, skipped is an array of objects { number, ticket }: the unsold / generated numbers the draw skipped before landing on a sold ticket.
The plugin persists the pulled payload as a JSON string in post meta under the key _vd_results. One important nuance: when stored by the plugin, each draw's skipped is normalised to a string[] of full ticket strings — not objects.
{
"competition": { "name": "Summer Cash Draw", "sourceId": "woo:1234" },
"draws": [
{
"rollIndex": 0,
"winner": { "number": 42, "ticket": "A042-UK" },
"publicCode": "VCODE-7Q2F",
"verifyUrl": "https://verifieddraws.uk/verify/VCODE-7Q2F",
"verified": true,
"revealedAt": "2026-06-13T20:14:00.000Z",
"skipped": [ "A007-UK", "A019-UK" ]
}
]
}The default path: automatic on-page display
Out of the box, the plugin renders the winning ticket and verify link on each competition (WooCommerce product) page the moment results are pulled — via woocommerce_after_single_product_summary on classic themes and a render_block filter on block themes. The operator controls this with the Show results on competition page toggle (option vd_show_results_on_competition, default on) under Verified Draws → Settings. Turn it off when you want to place the results yourself with one of the options below.
The easy path: the shortcode
Drop the [vd_results] shortcode into the page. An optional id attribute targets a specific post. The same markup is also auto-appended to the post content via the_content as a manual fallback.
[vd_results]
[vd_results id="1234"]The custom path: read the two fields yourself
Read _vd_results from post meta, decode the JSON, and loop draws. For each draw there are exactly two fields you need to render anywhere in a custom theme:
- The winning ticket —
draws[].winner.ticket(e.g."A042-UK"). The drawn ticket string to show as the result. - The verify link —
draws[].verifyUrl(e.g.https://verifieddraws.uk/verify/VCODE-7Q2F). The public, shareable URL anyone can open to confirm the draw against the beacon.
In the PHP below those two fields are read as $draw['winner']['ticket'] and $draw['verifyUrl'].
<?php
$raw = get_post_meta( get_the_ID(), '_vd_results', true );
$data = $raw ? json_decode( $raw, true ) : null;
if ( $data && ! empty( $data['draws'] ) ) :
echo '<div class="vd-results">';
foreach ( $data['draws'] as $draw ) :
$ticket = esc_html( $draw['winner']['ticket'] ?? '' );
$verify = esc_url( $draw['verifyUrl'] ?? '' );
?>
<p class="vd-results__winner">
Winning ticket: <strong><?php echo $ticket; ?></strong>
<a href="<?php echo $verify; ?>" rel="nofollow noopener" target="_blank">Verify this draw</a>
</p>
<?php if ( ! empty( $draw['skipped'] ) ) : ?>
<details>
<summary>Unsold / generated tickets skipped</summary>
<ul>
<?php foreach ( (array) $draw['skipped'] as $sk ) : ?>
<li><?php echo esc_html( (string) $sk ); ?></li>
<?php endforeach; ?>
</ul>
</details>
<?php endif;
endforeach;
echo '</div>';
endif;
?>The winner verify link points at /verify/<code> and the per-order ticket link at /verify/order/<code>-<start>-<count> — both are already public.
Build a Past Winners page in your theme
The single-page recipe above shows one competition's result on its own page. To build a site-wide “Past Winners” listing, run a WP_Query for every post that carries the _vd_results meta key, decode each stored payload, and render the competition name, the winning ticket (draws[].winner.ticket) and its verify link (draws[].verifyUrl). One query, one loop, every drawn competition on the site.
<?php
// Past Winners — list every competition that has a stored Verified Draws result.
$winners = new WP_Query( array(
'post_type' => 'product', // your competition post type
'posts_per_page' => -1,
'meta_key' => '_vd_results',
'meta_compare' => 'EXISTS',
'orderby' => 'date',
'order' => 'DESC',
) );
if ( $winners->have_posts() ) :
echo '<ul class="vd-past-winners">';
while ( $winners->have_posts() ) : $winners->the_post();
$data = json_decode( get_post_meta( get_the_ID(), '_vd_results', true ), true );
if ( empty( $data['draws'] ) ) { continue; }
foreach ( $data['draws'] as $draw ) :
$ticket = esc_html( $draw['winner']['ticket'] ?? '' );
$verify = esc_url( $draw['verifyUrl'] ?? '' );
?>
<li class="vd-past-winners__item">
<span class="vd-past-winners__comp"><?php the_title(); ?></span>
<span class="vd-past-winners__ticket">Winning ticket: <strong><?php echo $ticket; ?></strong></span>
<a class="vd-past-winners__verify" href="<?php echo $verify; ?>" rel="nofollow noopener" target="_blank">Verify this draw</a>
</li>
<?php
endforeach;
endwhile;
echo '</ul>';
wp_reset_postdata();
endif;
?>The meta_compare => 'EXISTS' clause selects only competitions that have already been drawn and pulled back, so the list stays clean. Each draws[].verifyUrl is a public, shareable link anyone can open to confirm the draw against the beacon.