# `Ltix.DeepLinking`
[🔗](https://github.com/DecoyLex/ltix/blob/main/lib/ltix/deep_linking.ex#L1)

Put content items into a signed response JWT for the platform.

When the platform sends an `LtiDeepLinkingRequest` launch, your tool
presents a selection UI and calls `build_response/3` with the chosen
items. The response JWT is then POSTed back to the platform's return URL.

Content items are built with these modules.

  * `Ltix.DeepLinking.ContentItem.LTIResourceLink` - an LTI activity
    the platform will link to
  * `Ltix.DeepLinking.ContentItem.Link` - an external URL
  * `Ltix.DeepLinking.ContentItem.File` - a downloadable file
  * `Ltix.DeepLinking.ContentItem.HTMLFragment` - inline HTML to embed
  * `Ltix.DeepLinking.ContentItem.Image` - an image for direct rendering

See the [Deep Linking](deep-linking.md) guide for the full workflow
and [Building Content Items](cookbooks/building-content-items.md) for
recipes.

# `build_response`

```elixir
@spec build_response(Ltix.LaunchContext.t(), list(), keyword()) ::
  {:ok, Ltix.DeepLinking.Response.t()} | {:error, Exception.t()}
```

Build a signed Deep Linking response JWT.

Takes a `LaunchContext` from an `LtiDeepLinkingRequest` launch, a list of
content items, and optional message/log fields. Returns a `Response` with
the signed JWT and the platform's return URL.

## Options

  * `:message` - user-facing message to show on return
  * `:log` - log message for the platform
  * `:error_message` - user-facing error message
  * `:error_log` - error log message for the platform
  * `:storage_adapter` - module implementing `Ltix.StorageAdapter`, which
    signs the response (defaults to application config)
  * `:storage_adapter_options` - options passed to the storage adapter's
    signing callbacks (defaults to `[]`)

## Errors

  * `Ltix.Errors.Invalid.InvalidMessageType` - context is not a deep
    linking launch
  * `Ltix.Errors.Invalid.InvalidContentItem` - an item is not a content
    item, has no `"type"` or one that is not a string, or `items` is not a
    list
  * `Ltix.Errors.Invalid.ContentItemTypeNotAccepted` - item type not
    in `accept_types`
  * `Ltix.Errors.Invalid.ContentItemsExceedLimit` - multiple items
    when `accept_multiple` is `false`
  * `Ltix.Errors.Invalid.LineItemNotAccepted` - line item present
    when `accept_line_item` is `false`
  * `Ltix.Errors.Invalid.PresentationTargetNotAccepted` - an item is
    presented in an `iframe`, `window`, or `embed` that is not in
    `accept_presentation_document_targets`
  * `Ltix.Errors.Invalid.MediaTypeNotAccepted` - a file's `media_type`
    does not match `accept_media_types`

Errors about one item include its `index` in `items`. Several failures at once
are grouped in `Ltix.Errors.Invalid`. Unknown or invalid options raise
`Ltix.Errors.Invalid.InvalidOption`.

## Examples

    {:ok, link} = Ltix.DeepLinking.ContentItem.LTIResourceLink.new(
      url: "https://tool.example.com/activity/123",
      title: "Quiz 1"
    )

    {:ok, response} = Ltix.DeepLinking.build_response(context, [link],
      message: "Selected 1 item"
    )

    # response.jwt — signed JWT to POST to response.return_url

See the [Deep Linking](deep-linking.md) guide for the full workflow.

# `build_response!`

```elixir
@spec build_response!(Ltix.LaunchContext.t(), list(), keyword()) ::
  Ltix.DeepLinking.Response.t()
```

Same as `build_response/3` but raises on error.

## Examples

    response = Ltix.DeepLinking.build_response!(context, [link], message: "Selected 1 item")

---

*Consult [api-reference.md](api-reference.md) for complete listing*
