Connect an Embedded App

How-To Guide: Issue Credentials from Embedded Apps in the LearnCard App Store

Build apps that run inside LearnCard and issue credentials directly to users. This guide covers the App Store integration for embedded applications that want to award badges, certificates, or other verifiable credentials.

Overview

The LearnCard App Store allows third-party applications to be embedded within the LearnCard app. These embedded apps can:

  • Authenticate users via Single Sign-On (SSO)

  • Issue credentials directly to the user's wallet

  • Request credentials for verification or gating

  • Request consent for data sharing agreements and terms acceptance

This is ideal for:

  • Learning platforms awarding course completion badges

  • Games issuing achievement credentials

  • Assessment tools providing certification

  • Any app that wants to reward users with verifiable credentials

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   LearnCard App                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚              Your Embedded App                 β”‚  β”‚
β”‚  β”‚                                               β”‚  β”‚
β”‚  β”‚   1. User completes action                    β”‚  β”‚
β”‚  β”‚   2. App calls sendCredential(templateAlias)  β”‚  β”‚
β”‚  β”‚   3. LearnCard issues from your template      β”‚  β”‚
β”‚  β”‚   4. User sees claim modal                    β”‚  β”‚
β”‚  β”‚   5. Credential stored in wallet              β”‚  β”‚
β”‚  β”‚                                               β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Prerequisites

  1. App Store Listing - Your app must be registered in the LearnCard App Store

  2. Boost Templates - Pre-configured credential templates attached to your listing

  3. Partner Connect SDK - For communication with the LearnCard host

Quick Start

Step 1: Install the SDK

Step 2: Initialize and Send a Credential

That's it! The user will see a claim modal and can accept the credential into their wallet.

Setting Up Your App Store Listing

1. Create Your App Listing

In the LearnCard Developer Portal, create a new app listing with:

  • Name & Description - What your app does

  • Launch URL - Where your embedded app is hosted

  • Permissions - Request credentials:write to issue credentials

  • Age Rating - Content rating for your app (optional)

  • Minimum Age - Minimum user age required to access your app (optional)

Age Restrictions

You can configure age-based access controls for your app:

Field
Type
Description

age_rating

'4+' | '9+' | '12+' | '17+'

Content rating similar to app store ratings. Indicates the maturity level of content.

min_age

number (0-18)

Minimum age (in years) required to access this app. Users below this age will not see or be able to launch the app.

circle-info

Age restrictions are enforced based on the user's date of birth in their profile

circle-exclamation
circle-info

Contract-based listings and managed profiles

If your listing's launch configuration includes a contractUri, the install flow will require guardian approval for managed/child profiles before the child can install/consent.

2. Create Credential Templates

Templates define what credentials your app can issue. For each type of credential:

  1. Go to your app listing's Templates tab

  2. Click Add Template

  3. Design your credential (name, description, image, achievement type)

  4. Note the Template Alias (auto-generated from name, e.g., course-completion)

The Template Alias is what you'll reference when issuing credentials from your app.

3. Configure Signing Authority

When you add a template to your listing, LearnCard automatically configures a signing authority. This allows credentials to be issued on behalf of your app with proper cryptographic signatures.

4. App Issuer Identity

App-issued credentials are signed as the app DID, not the integration owner's DID. The format is:

In the LearnCard App, credentials issued by apps display the app name and icon (with a link back to the app listing).

API Reference

sendCredential({ templateAlias, templateData? })

Issue a credential to the current user.

Parameters:

Parameter
Type
Required
Description

templateAlias

string

Yes

The template alias configured in your app listing

templateData

object

No

Values for template variables (e.g., {{name}})

Returns: Promise<TemplateCredentialResponse>

Example with Template Data:

Error Handling

initiateTemplateIssue(templateUri)

Let users send peer-to-peer badges to each other. Unlike sendCredential (which issues from your app to the current user), this opens a flow where the user selects a recipient from their contacts.

Parameters:

Parameter
Type
Required
Description

templateUri

string

Yes

The URI of the badge template to send

Returns: Promise<TemplateIssueResponse>

Example:

How it works:

  1. Your app calls initiateTemplateIssue with a template URI

  2. LearnCard opens a recipient picker for the user

  3. User selects someone from their contacts

  4. Badge is sent from the user to the recipient

Setting up peer badge templates:

  1. Go to your app listing's Templates tab

  2. Create a template and select Peer Badge as the type

  3. Copy the Template URI shown after creation

  4. Use this URI when calling initiateTemplateIssue

circle-info

Peer badges are sent from the user to another person, not from your app. This is ideal for recognition, gratitude, or social features within your app.

requestConsent(contractUri?, options?)

Request user consent for a ConsentFlow contract. This is useful when your app needs explicit user permission for data sharing, terms acceptance, or other consent-based flows.

circle-info

App Store Apps with Configured Contracts

If your app is installed from the LearnCard App Store and has a consent contract configured in its integration settings, you can omit the contractUri parameter. The SDK will automatically resolve the configured contract from your listing's integration.

Before (explicit contract URI):

After (using configured contract):

This simplifies your code and ensures users always consent to the correct contract for your app.

Parameters:

Parameter
Type
Required
Description

contractUri

string

No

The URI of the ConsentFlow contract. Can be omitted for App Store apps with configured contracts.

options

RequestConsentOptions

No

Additional options for the consent flow

Options:

Option
Type
Default
Description

redirect

boolean

false

If true, redirects to the contract's configured URL after consent granted

Returns: Promise<ConsentResponse>

Basic Example (App Store app with configured contract):

With Explicit Contract URI:

With Redirect:

How Redirect Works:

When redirect: true is set and the user grants consent:

  1. LearnCard generates a Verifiable Presentation (VP) proving consent

  2. User is redirected to the contract's redirectUrl

  3. The VP and user's DID are appended as URL parameters

  4. Your server can verify the VP to confirm consent

The redirect URL will include:

  • did - The user's DID

  • vp - A signed Verifiable Presentation (JWT format)

Use Cases:

  • Terms of Service - Require users to accept terms before accessing features

  • Data Sharing Agreements - Get explicit consent before sharing data with third parties

  • Privacy Policies - Track user acknowledgment of privacy policies

  • OAuth-like Flows - Use redirect mode for server-side consent verification

circle-info

If the user has already consented to a contract, calling requestConsent will return { granted: true } immediately without showing the consent modal again.

User Experience Flow

When your app issues a credential:

  1. Credential Created - LearnCard issues the credential using your boost template

  2. Claim Modal Appears - User sees a preview of the credential

  3. User Accepts - Credential is stored in their wallet

  4. Notification Updated - If user dismisses, they can claim later from notifications


Requesting Learner Context for AI

Embedded apps can request comprehensive learner context to power AI-driven experiences. This retrieves the user's credentials and personal data (with consent) and formats it for AI consumption.

When to Use Learner Context

  • AI Tutors - Adapt explanations based on learner's existing skills

  • Personalized Recommendations - Suggest content based on credential history

  • Smart Assessments - Adjust difficulty based on demonstrated competencies

  • Learning Pathways - Build custom paths from existing achievements

Prerequisites

  1. App Store Installation - Your app must be installed from the LearnCard App Store

  2. Consent Contract - Must have a consent contract configured in your listing's integration

  3. User Consent - User must have consented to share their data

Basic AI Tutor Integration

Using Structured Data

For custom AI integrations or when you need direct access to credentials:

Error Handling

Configuration Checklist

To use learner context in your app:

  1. Create a Consent Contract in LearnCard that defines what data your app needs

  2. Configure the Contract in your App Store listing's integration settings

  3. Request Consent using requestConsent() (without contractUri)

  4. Request Context using requestLearnerContext() after consent is granted

circle-info

The learner context feature respects user privacy. Users must explicitly consent to share their data, and they can revoke consent at any time through their LearnCard settings.

Recording AI Sessions

After using learner context to personalize an AI tutoring experience, you can record the session to create a structured learning history. AI Sessions appear in the user's AI Topics page, building a portfolio of their AI-assisted learning.

When to Record Sessions

  • After AI Tutoring - Document what was learned during the session

  • Skill Demonstrations - Record when the user shows competency

  • Learning Milestones - Mark significant learning achievements

  • Progress Tracking - Build a history of learning interactions

Session Structure

AI Sessions are organized hierarchically:

The AI Topic is automatically created on your app's first session. All subsequent sessions are organized under this same topic.

Basic Session Recording

Complete AI Tutor Example

Here's a complete example combining learner context and session recording:

Session Data Best Practices

title:

  • Short, scannable headline for the session

  • Reuse across sessionTitle if you have nothing fancier

summary:

  • One paragraph capturing what happened in the session

  • Written so the user (and the AI) can re-orient at a glance

learned (string array):

  • 3–5 main concepts gained, one per bullet

  • Use clear, concise language

  • Connect to practical applications

skills (array of { title, description }):

  • Group related competencies under a category title

  • Description explains what the user can now do

  • Use standard skill taxonomy when possible

nextSteps (array of { title, description, keywords? }):

  • Recommend specific follow-up activities

  • Vary types (courses, practice, assessments)

  • keywords is optional β€” only include it if you actually have taxonomy data; otherwise omit the field entirely

reflections (array of { title, description }):

  • Capture learner insights and "aha moments"

  • Record self-assessments

πŸŽ‰ Use isNewTopic for first-run UX

sendAiSessionCredential returns an isNewTopic: boolean. The first call from your app creates the AI Topic; subsequent calls reuse it. Use this flag to celebrate first-run, onboard the user, or show a different UI:

End-to-End AI Tutor Tutorial

For a complete, runnable AI Tutor walkthrough that wires requestConsent β†’ requestLearnerContext β†’ sendAiSessionCredential β†’ sendNotification together, see the working example app at examples/app-store-apps/4-request-learner-context-apparrow-up-right. It demonstrates:

  • Resolving the consent contract automatically from your App Store listing

  • Caching requestLearnerContext (it takes 2–5s in production β€” cache, render, revalidate)

  • Mapping AI tutor outputs to the real summaryData schema

  • Bridging notifications back into the wallet via the actionPath + launchFeature pattern

Tracking App State with Counters

Many embedded apps need to remember small bits of per-user state across sessions β€” "how many lessons has this user completed?", "what's their current streak?", "have they hit the threshold to unlock a credential yet?". The Partner Connect SDK provides a lightweight counter API for exactly this, scoped to (user, app) pairs and stored in the user's LearnCard account.

When to use counters

  • Progress tracking β€” lessons completed, quizzes passed, sessions recorded

  • Streaks β€” daily-use streaks, consecutive-correct streaks

  • Threshold-gated credential issuance β€” "issue the badge after 10 sessions"

  • Lightweight feature flags β€” "has this user seen the onboarding tour?" (use 0 / 1)

When not to use counters

  • Anything not an integer β€” values must be whole numbers. Pre-aggregate fractional state.

  • High-cardinality keys β€” there's a hard cap of 50 distinct keys per (user, app). If you'd need a counter per lesson ID, consolidate instead (e.g. one lessons_completed counter, plus a separate credential or sendAiSessionCredential call to record specifics).

  • High-frequency writes β€” there's a 100 writes/minute per (user, app) rate limit. Don't increment a counter on every keystroke; debounce or batch on the client and increment once per meaningful event.

  • Cross-user state β€” counters are scoped per-user. There is no shared/global counter.

  • Sensitive or large data β€” counters store integers only.

Limits (load-bearing β€” design around these)

Limit
Value

Distinct keys per user-app

50

Writes per minute per user-app

100

Value type

Signed integer only

Key character set

^[a-zA-Z0-9_-]+$ (alphanumeric, _, -)

Key length

1–64 characters

Batch read (getCounters)

Up to 50 keys per call

API

The SDK exposes three methods. All take positional arguments (not options objects).

Response shapes:

A counter that has never been incremented reads as value: 0, updatedAt: null from getCounter. You don't need to "create" a counter β€” the first incrementCounter call brings it into existence atomically.

circle-exclamation

Example: simple progress tracking

Example: threshold-gated credential issuance

A common pattern is "issue a badge once the user crosses a threshold". Because incrementCounter returns the new value atomically, you can gate issuance on the response without a separate read:

circle-info

Why use previousValue < threshold && newValue >= threshold? This guarantees the badge is issued exactly once even if the user hits the threshold, you call again, and the counter has already moved past it. It's the idempotent way to react to "the moment of crossing".

Example: daily streaks

This is intentionally illustrative β€” for serious streak logic you'd record the date as part of an AI Session credential, not as a counter. Counters shine for simple monotonic counts.

Error handling

Best practices

  • Pick stable, well-named keys. Treat counter keys like database column names β€” once your app is in production, renaming a key effectively zeroes out everyone's history.

  • Consolidate aggressively. Prefer one lessons_completed counter over lesson_1_complete, lesson_2_complete, … (the 50-key cap is unforgiving).

  • Pair counters with credentials, don't replace them. Counters are for cheap aggregate state. The actual record of what the user did should still be a credential (sendCredential, sendAiSessionCredential).

  • Debounce writes on the client. If a user can tap a button 30 times in 5 seconds, increment once with amount: 30 instead of calling 30 times.

  • Use incrementCounter's return value rather than read-then-write β€” it's atomic, so you avoid race conditions and save a round-trip.

Complete Example

Here's a full example of a simple embedded app:

Best Practices

1. Design Meaningful Credentials

  • Use clear, descriptive names

  • Include relevant achievement details

  • Add appealing images/icons

  • Set appropriate achievement types (Badge, Certificate, etc.)

2. Issue at the Right Moment

  • Award credentials immediately after achievement

  • Don't spam users with too many credentials

  • Consider combining small achievements into milestone badges

3. Handle Errors Gracefully

  • Always wrap credential issuance in try/catch

  • Provide feedback to users on success/failure

  • Log errors for debugging

4. Test Thoroughly

  • Use the App Preview feature in the Developer Portal

  • Test with different user states (logged in, logged out)

  • Verify credentials appear correctly in the wallet

Testing Your Integration

Using the Developer Portal Preview

  1. Go to your app listing in the Developer Portal

  2. Click Preview App

  3. Your app loads in a test iframe

  4. Test credential issuance - credentials go to your wallet

Local Development

For local development, use the Developer Portal's Preview App feature which provides a test iframe environment. Your app will be loaded within LearnCard and credentials will be issued to your test wallet.

Last updated

Was this helpful?