First Aid Embed Integration
WordPress integration · First Aid V2

First Aid Embed Integration

Replace the first-aid PDF links with interactive guides served in an <iframe>. Three steps: read the published guides from the Parse REST API, join them to the PDF rows you already list, then render an iframe where a guide exists and keep the PDF where it doesn't.

01

The embed snippet

One guide per iframe. id is the guide's slug — step 2 is how you get it.

<iframe
  src="https://casualty-staging.b4a.app/embed/?id=choking"
  style="width:100%;height:800px;border:0;max-width:480px"
  loading="lazy"
  title="First Aid: Choking"
></iframe>

Staging domain. Ask engineering for the production domain before go-live.

The URL form is not negotiable

It must be /embed/?id=choking — with the trailing slash on /embed/ and the slug in the query string. /embed/choking returns 403 {"error":"unauthorized"} and renders that text inside your iframe: the host serves static files only and has no single-page-app fallback, so a slug in the path matches no file. /embed?id=… (no slash) works but costs a 301 redirect.

Height is fixed by the iframe, not the content — 700–900px suits most guides, and the guide scrolls internally if it's taller. max-width:480px keeps the phone-shaped layout from stretching. Guides are served noindex, so no SEO value transfers to the host page.

02

Fetch the published guides

Everything lives in one Parse class, FirstAidItems, which holds both the old PDF rows and the new interactive guides. Filter to the interactive ones that are live:

curl -sG https://parseapi.back4app.com/classes/FirstAidItems \
  -H "X-Parse-Application-Id: $PARSE_APP_ID" \
  -H "X-Parse-REST-API-Key: $PARSE_REST_KEY" \
  --data-urlencode 'where={"status":"published","renderKind":"algoV2"}' \
  --data-urlencode 'keys=v2Id,name,supersedes,faTypeArr' \
  --data-urlencode 'limit=1000'

Returns 45 guides today. Ask engineering for the keys — never commit them.

Each result gives you the four fields that matter:

{
  "objectId":   "kyIw3XIpNe",             // stable row id — the safe fallback key
  "v2Id":       "choking",                // the slug you put in ?id=
  "name":       "Choking",                // display title
  "supersedes": ["x0VwLO6veY"],           // objectIds of the PDF rows this replaces
  "faTypeArr":  ["life"]                  // life | medical | trauma | poison | paeds
}
Three things that will bite you
  • Pass limit. Parse defaults to 100 rows and silently truncates.
  • Always filter status. The class is publicly readable at every status, so an unfiltered query returns drafts and archived guides too. Only published renders — anything else shows "This guide is not available."
  • v2Id is not unique. Two published rows currently share the slug recovery_position, and which one loads is undefined. The embed also accepts an objectId in ?id=, so use ?id=KcrliuBslH for that guide — or any guide, if you prefer one key for everything.

Calling this from browser JavaScript works — the API sends Access-Control-Allow-Origin: * — but prefer fetching server-side in PHP and caching, so you aren't hitting Parse on every page view and aren't shipping a key in your theme:

function fa_guides() {
  if ( $hit = get_transient( 'fa_guides' ) ) return $hit;

  $qs = http_build_query( [
    'where' => wp_json_encode( [ 'status' => 'published', 'renderKind' => 'algoV2' ] ),
    'keys'  => 'v2Id,name,supersedes,faTypeArr',
    'limit' => 1000,
  ] );

  $res = wp_remote_get( "https://parseapi.back4app.com/classes/FirstAidItems?$qs", [
    'headers' => [
      'X-Parse-Application-Id' => PARSE_APP_ID,
      'X-Parse-REST-API-Key'   => PARSE_REST_KEY,
    ],
    'timeout' => 15,
  ] );
  if ( is_wp_error( $res ) ) return [];

  $rows = json_decode( wp_remote_retrieve_body( $res ), true )['results'] ?? [];
  set_transient( 'fa_guides', $rows, HOUR_IN_SECONDS );
  return $rows;
}
03

Join the guides to your PDF list

Your existing list is built from the legacy rows — the ones with a faFile PDF and no renderKind. Don't match on name: only 15 of 42 titles match exactly ("Recovery position (Adults & Children)" versus "Recovery Position", and so on).

The authoritative join is supersedes: each guide lists the objectId of every PDF row it replaces. Invert it once into legacy objectId → slug, then look up each row you already render:

$replaces = [];                                  // legacy objectId => guide
foreach ( fa_guides() as $g ) {
  foreach ( $g['supersedes'] ?? [] as $legacy_id ) {
    $replaces[ $legacy_id ] = $g;
  }
}

// $item is a legacy row you already list
if ( isset( $replaces[ $item['objectId'] ] ) ) {
  $slug = $replaces[ $item['objectId'] ]['v2Id'];
  printf(
    '<iframe src="https://casualty-staging.b4a.app/embed/?id=%s" '
    . 'style="width:100%%;height:800px;border:0;max-width:480px" '
    . 'loading="lazy" title="First Aid: %s"></iframe>',
    rawurlencode( $slug ), esc_attr( $item['name'] )
  );
} else {
  fa_render_pdf_link( $item );                   // no guide yet — keep the PDF
}

Where that lands against today's data:

36PDFs become iframes
6PDFs stay, no guide yet
9New guides, never had a PDF
45Published guides total

So the fallback branch is not hypothetical — six of your current entries have no interactive replacement and must keep working as PDFs:

PDF entryobjectIdCategoryRender as
DehydrationjXiTsbA0sZmedicalpdf
Evaluation (Babies)zYdtBiyhj0paedspdf
Fever (Babies)xE3M00XmZapaedspdf
Injuries: BitesJL19GAhCchtraumapdf
Poisoning (Babies)uTR5OkvX5opaedspdf
Rash68J7BtMi4Jmedicalpdf

Nine guides your list is missing

These have no supersedes because they never existed as PDFs. The join above will never surface them — add them to the list explicitly, or build the list from the guides rather than from the PDF rows.

Guide?id=CategoryRender as
Animal or Human Bitesanimal_or_human_bitestraumaiframe
Bruisebruisetraumaiframe
CPRcpriframe
Helmet Removalhelmet_removaltraumaiframe
Muscle Crampsmuscle_crampstraumaiframe
Newborn Resuscitationnewborn_resuscitationpaedsiframe
Recovery PositionKcrliuBslHlifeiframe
Scorpion or Spider Bitesscorpion_or_spider_bitestraumaiframe
Snake Bitessnake_bitestraumaiframe

Recovery Position is keyed by objectId because its slug is duplicated — see step 2.