Common content item patterns for deep linking responses. Each section shows a self-contained example you can adapt.
LTI resource link
The simplest content item. The platform creates a link that launches back into your tool.
{:ok, link} = Ltix.DeepLinking.ContentItem.LTIResourceLink.new(
url: "https://tool.example.com/activities/42",
title: "Week 3 Activity"
)Omit url to use the tool's base launch URL configured in the
platform.
{:ok, link} = Ltix.DeepLinking.ContentItem.LTIResourceLink.new(
title: "Activity from Tool"
)Resource link with a line item
Include a line_item so the platform auto-creates a gradebook column.
You can then post scores to it using the grade service.
{:ok, link} = Ltix.DeepLinking.ContentItem.LTIResourceLink.new(
url: "https://tool.example.com/quizzes/7",
title: "Midterm Quiz",
line_item: [
score_maximum: 100,
label: "Midterm Quiz",
tag: "quiz",
grades_released: true
]
)score_maximum is required and must be positive. The label defaults
to the content item's title if omitted, and must not be blank when
given.
line_item also accepts an Ltix.GradeService.LineItem. Deep linking can only send label, score_maximum, resource_id, tag, and grades_released, so a line item that sets any other field, such as id, returns an error.
Resource link with custom parameters
Custom parameters are passed to your tool on every launch of the created link. Pass them as a keyword list or a map with string or atom keys. Values must be strings.
{:ok, link} = Ltix.DeepLinking.ContentItem.LTIResourceLink.new(
url: "https://tool.example.com/assignments",
title: "Essay Submission",
custom: [
assignment_id: "essay-3",
rubric: "standard",
allow_late: "true"
]
)Setting availability windows
Control when the link is accessible and when submissions are accepted.
{:ok, link} = Ltix.DeepLinking.ContentItem.LTIResourceLink.new(
url: "https://tool.example.com/exams/final",
title: "Final Exam",
available: [
start_date_time: ~U[2025-05-01 08:00:00Z],
end_date_time: ~U[2025-05-01 12:00:00Z]
],
submission: [
end_date_time: ~U[2025-05-01 11:30:00Z]
]
)Both fields in each window are optional. You can set just an end time and leave the start unset.
External link
For URLs outside your tool. Use Link when the resource isn't an LTI
launch.
{:ok, link} = Ltix.DeepLinking.ContentItem.Link.new(
url: "https://docs.example.com/getting-started",
title: "Getting Started Guide",
icon: [url: "https://docs.example.com/favicon.png", width: 32, height: 32]
)Control how the platform opens the link with window or iframe.
{:ok, link} = Ltix.DeepLinking.ContentItem.Link.new(
url: "https://videos.example.com/lecture-5",
title: "Lecture 5 Recording",
iframe: [src: "https://videos.example.com/embed/lecture-5", width: 800, height: 450]
)Include embeddable HTML with embed.
{:ok, link} = Ltix.DeepLinking.ContentItem.Link.new(
url: "https://interactive.example.com/sim/42",
title: "Physics Simulation",
embed: [html: ~s(<iframe src="https://interactive.example.com/embed/42"></iframe>)]
)File with expiry
File URLs are expected to be short-lived. Set expires_at to send the
platform the time the URL stops working.
{:ok, file} = Ltix.DeepLinking.ContentItem.File.new(
url: "https://tool.example.com/exports/report-abc123.pdf",
title: "Lab Report Template",
media_type: "application/pdf",
expires_at: ~U[2025-03-15 12:00:00Z]
)HTML fragment
Inline HTML the platform embeds directly in its page.
{:ok, fragment} = Ltix.DeepLinking.ContentItem.HTMLFragment.new(
html: """
<div class="tool-widget">
<h3>Study Flashcards</h3>
<p>25 cards covering Chapter 4</p>
</div>
""",
title: "Chapter 4 Flashcards"
)Prefer Link with embed
If your HTML renders a single resource that also has a direct URL,
use Link with an embed. This gives the platform both the
embeddable HTML and a fallback URL.
Image
An image the platform renders directly with an img tag.
{:ok, image} = Ltix.DeepLinking.ContentItem.Image.new(
url: "https://tool.example.com/diagrams/architecture.png",
title: "System Architecture",
width: 1200,
height: 800
)Include a thumbnail for list views.
{:ok, image} = Ltix.DeepLinking.ContentItem.Image.new(
url: "https://tool.example.com/photos/lab-setup.jpg",
title: "Lab Setup Photo",
thumbnail: [
url: "https://tool.example.com/photos/lab-setup-thumb.jpg",
width: 150,
height: 100
]
)Using extensions
Add vendor-specific properties keyed by fully qualified URLs. Extensions
are merged into the top-level JSON output. A key that is a standard
field, such as type or url, returns an error.
{:ok, link} = Ltix.DeepLinking.ContentItem.LTIResourceLink.new(
url: "https://tool.example.com/activity/5",
title: "Proctored Exam",
extensions: %{
"https://vendor.example.com/proctoring" => %{
"required" => true,
"duration_minutes" => 90
}
}
)All five content item types support the extensions field.
Returning multiple items
Pass a list of mixed content item types to build_response/3.
{:ok, quiz} = Ltix.DeepLinking.ContentItem.LTIResourceLink.new(
url: "https://tool.example.com/quizzes/1",
title: "Quiz 1",
line_item: [score_maximum: 50]
)
{:ok, reading} = Ltix.DeepLinking.ContentItem.Link.new(
url: "https://docs.example.com/chapter-3",
title: "Chapter 3 Reading"
)
{:ok, response} = Ltix.DeepLinking.build_response(context, [quiz, reading],
message: "Added 2 items"
)Check settings.accept_multiple before offering multi-select. If
accept_multiple is false, build_response/3 returns a
ContentItemsExceedLimit error for more than one item.
Returning no items
Return an empty list with a message when the user cancels or an error occurs.
# User cancelled
{:ok, response} = Ltix.DeepLinking.build_response(context, [],
message: "No items selected"
)
# Something went wrong
{:ok, response} = Ltix.DeepLinking.build_response(context, [],
error_message: "Could not load available activities",
error_log: "ActivityService returned 503"
)An empty items list is always valid regardless of platform constraints.