Developer docs

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.

Install & connect the plugin (site token)

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).

Push the roster

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.

Ticket generation

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).

PHP
$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>.

Per-order verify URL
https://verifieddraws.uk/verify/order/VCODE-7Q2F-0-3
Results pull-back endpoint contract

The 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
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
curl -X POST https://verifieddraws.uk/api/plugin/results \
  -H "Authorization: Bearer $VD_SITE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
Response — { competition, draws }
{
  "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 _vd_results meta key

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.

_vd_results (stored JSON shape)
{
  "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" ]
    }
  ]
}
Render the links in a custom theme template

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.

Shortcode
[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 — custom theme template
<?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 — a Past Winners archive template
<?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.