Skip to content

In-App Integration

This guide covers embedding your Canviq board directly in your app so users never leave to submit feedback, vote, or check the roadmap.


Prerequisites

  • Your Canviq org slug: find it in your board URL: https://{your-slug}.canviq.app/en

Embedded Web Form

Embed your Canviq board directly in your app. Users get the full board experience: submitting ideas, voting, viewing the roadmap, without leaving your app.

This requires no backend code.

iOS (SFSafariViewController)

import SafariServices

let ORG_SLUG = "your-slug"  // replace with your board slug

// Open the full feedback board
func openFeedbackBoard() {
    let url = URL(string: "https://\(ORG_SLUG).canviq.app/en")!
    let safari = SFSafariViewController(url: url)
    present(safari, animated: true)
}

// Open with the submit form pre-focused
func openSubmitFeedback() {
    let url = URL(string: "https://\(ORG_SLUG).canviq.app/en/submit")!
    let safari = SFSafariViewController(url: url)
    present(safari, animated: true)
}

// Open the public roadmap
func openRoadmap() {
    let url = URL(string: "https://\(ORG_SLUG).canviq.app/en/roadmap")!
    let safari = SFSafariViewController(url: url)
    present(safari, animated: true)
}

!!! important "Use SFSafariViewController, not WKWebView" WKWebView does not share the Safari cookie jar. Users will not be able to sign in or have their session persist. Always use SFSafariViewController.

Android (Chrome Custom Tabs)

import androidx.browser.customtabs.CustomTabsIntent

val ORG_SLUG = "your-slug"  // replace with your board slug

// Open the full feedback board
fun openFeedbackBoard() {
    val url = Uri.parse("https://$ORG_SLUG.canviq.app/en")
    val intent = CustomTabsIntent.Builder().build()
    intent.launchUrl(this, url)
}

// Open with the submit form pre-focused
fun openSubmitFeedback() {
    val url = Uri.parse("https://$ORG_SLUG.canviq.app/en/submit")
    CustomTabsIntent.Builder().build().launchUrl(this, url)
}

// Open the public roadmap
fun openRoadmap() {
    val url = Uri.parse("https://$ORG_SLUG.canviq.app/en/roadmap")
    CustomTabsIntent.Builder().build().launchUrl(this, url)
}

Web (iframe)

<!-- Full board embed -->
<iframe
  src="https://{your-slug}.canviq.app/en"
  style="width: 100%; height: 600px; border: none;"
  title="Product feedback"
  loading="lazy"
/>

<!-- Submit form pre-opened -->
<iframe
  src="https://{your-slug}.canviq.app/en/submit"
  style="width: 100%; height: 600px; border: none;"
  title="Submit feedback"
/>

If your app uses a Content Security Policy, add *.canviq.app to your frame-src directive:

Content-Security-Policy: frame-src https://*.canviq.app;

Control which screen opens by navigating to the appropriate path:

URL Opens
https://{your-slug}.canviq.app/en Default board (submission list)
https://{your-slug}.canviq.app/en/submit Submission form
https://{your-slug}.canviq.app/en/roadmap Public roadmap
https://{your-slug}.canviq.app/en/submissions/{id} Specific submission detail

Authentication in the Embedded Board

Users authenticate via magic link. Canviq sends a one-time login link to their email. Once clicked, the session persists across future launches (cookies are shared with the system browser).

!!! info "Pre-authentication (coming soon)" Authenticated embed tokens will allow you to pre-authenticate your users so they arrive at the board already signed in. Contact support@canviq.app if this is blocking your integration.


There Is No Public Write API

There is no REST endpoint to submit feedback, vote, or comment on a user's behalf from your own backend today. Everything happens inside the embedded board itself, using the flows above. See Votes API for the reasoning behind not building one speculatively.


Rate Limits

Scenario Limit
Per-user via embedded board 5 submissions / hour

Questions

Contact support@canviq.app.