Autopilot rebranded to Ortto in 2021. To integrate Ortto (formerly Autopilot) with V0, generate your signup or lead capture UI in V0, then create a Next.js API route that calls the Ortto REST API to add contacts and trigger customer journeys. Store your Ortto API key in Vercel environment variables. This lets you enroll users into automated email sequences and visual customer journeys directly from your V0-built app.
Automating Customer Journeys from Your V0 App with Ortto (Autopilot)
Ortto, which rebranded from Autopilot in 2021, is built around the concept of visual customer journeys — drag-and-drop workflows that send emails, SMS messages, and in-app notifications based on what contacts do and when. When you connect your V0 app to Ortto, every form submission, signup, or user action can automatically enroll a contact into the right journey at the right time.
The integration pattern is simple: your V0-generated form collects user information, your Next.js API route sends it to Ortto's People API to create or update a contact record, and Ortto's automation engine takes it from there. You can trigger specific journeys by adding a journey trigger to your API call, or let Ortto's rules engine decide which journey to enroll the contact in based on their attributes and behavior.
A key detail to know before starting: Ortto uses data sources to organize contacts, and each data source has its own API key. When you generate your API key in the Ortto dashboard, make sure you are generating it for the correct data source — typically your primary Ortto data source. The API endpoints also differ by region (US, EU, AU), so confirm your account's region in the Ortto dashboard to use the correct base URL.
Integration method
V0 generates the lead capture or signup UI while a Next.js API route handles the Ortto API calls server-side. When a user submits a form in your V0 app, the frontend POSTs to your API route, which calls the Ortto REST API to create or update a person record and optionally trigger a journey. The Ortto API key stays exclusively in server-side Vercel environment variables and is never exposed to the browser.
Prerequisites
- A V0 account with a Next.js project at v0.dev
- An Ortto account at ortto.com (formerly Autopilot) with at least one data source configured
- An Ortto API key generated for your data source in Ortto Settings → API keys
- The Ortto API base URL for your account region (US: api.ap3api.com, EU: eu.ap3api.com, AU: au.ap3api.com)
- Basic knowledge of which Ortto journeys or triggers you want to activate from form submissions
Step-by-step guide
Get Your Ortto API Key and Region
Get Your Ortto API Key and Region
Ortto's API authentication uses a per-data-source API key passed as a header in every request. Finding the correct key and base URL requires navigating to the right section of the Ortto dashboard. Log in to Ortto at app.ortto.com. In the left sidebar, click Settings (the gear icon), then select API keys. You will see a list of API keys associated with your account's data sources. If you have not created an API key yet, click New API key, select the data source you want to connect to (typically your main data source), and copy the generated key. Next, identify your account's region. Ortto is deployed in three regions: US, EU, and AU. Your account's region is displayed in the Ortto dashboard URL — if your URL is app.ortto.com it is US, if it shows eu.ortto.com it is EU. The base URL for your API calls will be: - US: https://api.ap3api.com - EU: https://eu.ap3api.com - AU: https://au.ap3api.com Using the wrong region URL will result in authentication failures even with a valid API key, so this is important to get right. Also note the IDs of any journeys you want to trigger programmatically. In Ortto, open the journey, click on the journey trigger block, and look for an entry trigger type called 'API trigger'. The journey needs to have an API trigger entry point configured for your API calls to enroll contacts into it. The journey ID is visible in the URL when you have the journey open.
Pro tip: Create a separate Ortto API key for each environment (development, production) so you can point local development to a test data source and prevent test contacts from polluting your production Ortto lists.
Expected result: You have your Ortto API key, confirmed your account region, and know the base API URL to use in your Next.js API routes.
Generate the Lead Capture UI with V0
Generate the Lead Capture UI with V0
Use V0 to generate the frontend form or signup component that will capture user information and send it to Ortto. V0 produces polished, accessible form components with Tailwind CSS and shadcn/ui that you can customize directly in the V0 chat. When writing your V0 prompt, describe exactly which fields you need, what validation you want (required fields, email format, etc.), and what the success and error states should look like. Be explicit about the API endpoint the form should submit to — your planned /api/ortto/signup or /api/ortto/lead-capture route. For multi-step forms or forms with conditional fields (e.g., show a company name field only when the user selects 'Business' for account type), V0 can generate these with React state management. Keep the form logic client-side and only send the final complete submission to the API route. One V0 limitation to keep in mind: V0 generates the form submission logic using fetch() but may not handle all edge cases like network timeouts or duplicate submissions. Review the generated submission handler to ensure it disables the submit button during the API call and re-enables it on both success and error to prevent duplicate submissions.
Create a newsletter signup component with Email and First Name fields. Validate email format client-side before submitting. On submit, POST to /api/ortto/subscribe with { email, firstName }. Show a loading spinner on the button during submission. On success, replace the form with a friendly 'You are on the list!' confirmation. On error, show a red alert with the message 'Something went wrong. Please try again.' Keep the design minimal and suitable for embedding in a landing page footer.
Paste this in V0 chat
Pro tip: Ask V0 to add honeypot spam protection to the form — a hidden field that bots fill in but real users do not. Check for this field in your API route and reject submissions where it is not empty.
Expected result: V0 generates a functional form component with client-side validation, loading states, and success/error UI that calls your planned Ortto API route.
Create the Next.js API Route for Ortto
Create the Next.js API Route for Ortto
Create an API route at app/api/ortto/subscribe/route.ts (or the path that matches your frontend's fetch call). This route receives form data from the browser, validates it server-side, then calls the Ortto People API to create or update a person record. The Ortto People API endpoint is POST /v1/person/merge — this endpoint creates a new person if the email does not exist, or updates the existing person record if it does. The request body uses an 'attributes' object where contact data is stored using Ortto's field format: field names are prefixed with their type (str:, dbl:, int:, bool:, dte:, geo:). Standard fields like email use the key str::email, first name uses str::first (these exact field names are required by Ortto's API). The API key is passed as the X-Api-Key header. All requests must be HTTPS. If you want to trigger an Ortto journey when the contact is created, include a 'trigger_journey_run' field in the request with the journey's trigger ID. Handle rate limiting gracefully: Ortto's API has rate limits that vary by plan. If you receive a 429 response, return a 429 to the frontend with a user-friendly error message. Also validate the email format server-side in the API route even if the frontend validates it too — defense in depth prevents malformed data from reaching Ortto.
Create a Next.js API route at app/api/ortto/subscribe/route.ts. On POST, parse request body for email and firstName fields. Validate that email is present and is a valid email format. Call Ortto People API at https://api.ap3api.com/v1/person/merge with header X-Api-Key set to process.env.ORTTO_API_KEY. Request body should contain: { async: false, merge_by: ['str::email'], people: [{ fields: { 'str::email': email, 'str::first': firstName } }] }. Return 200 with { success: true } on success. Handle errors with appropriate status codes.
Paste this in V0 chat
1// app/api/ortto/subscribe/route.ts2import { NextRequest, NextResponse } from 'next/server';34const ORTTO_BASE_URL = process.env.ORTTO_BASE_URL || 'https://api.ap3api.com';56export async function POST(request: NextRequest) {7 try {8 const body = await request.json();9 const { email, firstName } = body;1011 if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {12 return NextResponse.json(13 { error: 'Valid email is required' },14 { status: 400 }15 );16 }1718 const orttoPayload = {19 async: false,20 merge_by: ['str::email'],21 people: [22 {23 fields: {24 'str::email': email,25 ...(firstName ? { 'str::first': firstName } : {}),26 },27 },28 ],29 };3031 const response = await fetch(`${ORTTO_BASE_URL}/v1/person/merge`, {32 method: 'POST',33 headers: {34 'Content-Type': 'application/json',35 'X-Api-Key': process.env.ORTTO_API_KEY!,36 },37 body: JSON.stringify(orttoPayload),38 });3940 if (!response.ok) {41 const errorText = await response.text();42 console.error('Ortto API error:', response.status, errorText);43 return NextResponse.json(44 { error: 'Failed to subscribe' },45 { status: 500 }46 );47 }4849 return NextResponse.json({ success: true });50 } catch (error) {51 console.error('Subscribe route error:', error);52 return NextResponse.json(53 { error: 'Internal server error' },54 { status: 500 }55 );56 }57}Pro tip: Set async: false in the Ortto API payload to get a synchronous response confirming the contact was created. Setting async: true is faster but means you cannot detect API-level errors like invalid field names in the response.
Expected result: POST /api/ortto/subscribe successfully creates a contact in your Ortto data source. The new person appears in Ortto → People within a few seconds.
Add Environment Variables in Vercel
Add Environment Variables in Vercel
Your Ortto API route requires environment variables for authentication and the correct regional API base URL. Add them in Vercel Dashboard → Settings → Environment Variables with scope set to Production, Preview, and Development. ORTTO_API_KEY: the API key you generated in Ortto Settings → API keys. This is a server-side secret — never add a NEXT_PUBLIC_ prefix. The Ortto API key gives full read and write access to your Ortto data source, so treat it with the same care as a database password. ORTTO_BASE_URL: the base URL for your Ortto account's region. Use https://api.ap3api.com for US accounts, https://eu.ap3api.com for EU accounts, or https://au.ap3api.com for AU accounts. Making this an environment variable rather than hardcoding it in your route means you can easily switch regions if needed and avoid hardcoding a URL that might change. For local development, create a .env.local file with these same variables. V0-generated Next.js projects include .env.local in .gitignore, so your credentials will not be committed to GitHub. After saving the variables in Vercel, trigger a redeployment. Test the integration end-to-end by submitting the form on your live Vercel URL and then checking Ortto → People to confirm the contact appeared.
1# .env.local (for local development only — never commit this file)2ORTTO_API_KEY=your_ortto_api_key_here3ORTTO_BASE_URL=https://api.ap3api.comPro tip: In Ortto, you can create custom fields to store app-specific data like 'Signup Source' or 'Plan Type'. Add them to your API route payload using the appropriate field prefix (str:: for text, dbl:: for numbers, bool:: for booleans) to track where contacts came from.
Expected result: Vercel shows both environment variables saved. The subscribe API route authenticates successfully and contacts appear in Ortto within seconds of form submission.
Common use cases
SaaS Trial Signup Onboarding
A SaaS app built in V0 has a trial signup form. When a user signs up, the V0 form POSTs to an API route that creates the contact in Ortto with their email, name, and trial start date, then triggers a 7-day onboarding email journey designed to activate new trial users.
Create a trial signup form with fields: First Name, Last Name, Email, Company Name, and Company Size (select: 1-10, 11-50, 51-200, 200+). On submit, POST to /api/ortto/signup with the form data as JSON. Show a success message 'Welcome to the trial! Check your email.' or an error alert if the submission fails. Disable the submit button and show a spinner while submitting.
Copy this prompt to try it in V0
Lead Magnet Download and Nurture
A marketing page offers a free guide download. V0 generates the landing page with an email capture form. When the user submits their email, the API route adds them to Ortto and triggers a lead nurture journey that delivers the guide link and follows up with educational content over the next two weeks.
Build a lead capture landing page for a free guide download. Include a headline, a brief description of the guide, and a simple form with Email and First Name fields. On submit, POST to /api/ortto/lead-capture. Show a thank-you state that says 'Your guide is on its way!' and hides the form. Use a clean, minimal design with a prominent CTA button.
Copy this prompt to try it in V0
Event Registration with Follow-Up Sequence
A community platform uses V0 to build an event registration page. When someone registers, the API route creates or updates their Ortto contact record with the event name and registration date, triggering a journey that sends confirmation, reminders, and post-event follow-up emails.
Create an event registration form with fields: Full Name, Email, Job Title, and a checkboxes list of session tracks they want to attend. On submit, POST to /api/ortto/event-register. Show a confirmation card with the event details and a calendar invite download link after successful registration. Handle duplicate email submissions gracefully.
Copy this prompt to try it in V0
Troubleshooting
API route returns 401 Unauthorized when calling Ortto
Cause: The ORTTO_API_KEY environment variable is missing, incorrect, or the key was generated for a different Ortto data source than the one you are trying to write to.
Solution: Go to Ortto Settings → API keys and verify the key is still active and associated with the correct data source. Confirm the key in Vercel Dashboard → Settings → Environment Variables matches exactly. Redeploy after updating the variable.
API returns 200 but contacts do not appear in Ortto People
Cause: The request used async: true and the contact creation is queued but delayed, or the merge_by field is incorrectly specified so Ortto cannot match the person to create/update.
Solution: Change async: false in your Ortto API payload to get a synchronous response. Also confirm that merge_by is set to ['str::email'] and the 'str::email' field in your people array has a valid email value.
Custom fields I added (like Company or Plan Type) are not saving to the Ortto contact
Cause: Custom fields use a different field name prefix in the Ortto API — Ortto generates a unique field ID for custom fields that differs from the display name. The field key is not simply str::companyName.
Solution: In Ortto, go to Data → Fields and find your custom field. Click on it to see its API field name (shown as the 'Key' or 'API name' property). Use this exact key in your API payload instead of guessing the format.
1// Example with custom Ortto field keys2fields: {3 'str::email': email,4 'str::first': firstName,5 'str:cm:company--name': companyName, // actual custom field key from Ortto UI6}CORS error in the browser when the form submits
Cause: The frontend is calling the Ortto API directly instead of going through the Next.js API route, triggering Ortto's CORS restrictions on browser-side requests.
Solution: Ensure all Ortto API calls go through your /api/ortto/* Next.js routes — never call the Ortto API directly from the browser. The Next.js API route runs server-side and is not subject to CORS restrictions.
Best practices
- Always proxy Ortto API calls through a Next.js API route and never expose your ORTTO_API_KEY client-side — a leaked Ortto API key gives full access to your entire contact database and journeys
- Use merge_by: ['str::email'] in the person/merge endpoint to prevent duplicate contacts — Ortto will update existing records instead of creating duplicates when the same email submits multiple times
- Validate email format server-side in your API route as a second layer of defense, even if the frontend form also validates it
- Add the contact's signup source as a custom field (e.g., 'Signup Source: V0 App Landing Page') to segment contacts by acquisition channel in Ortto
- Use environment-specific Ortto API keys to separate development test contacts from production contacts — use a test data source in development
- Include meaningful tags when creating contacts (Ortto supports tagging via the API) to make it easy to filter and segment contacts based on which part of your app they came from
- Handle Ortto API errors gracefully in your route and return user-friendly error messages to the frontend — avoid exposing raw API error details which may contain information about your integration structure
Alternatives
Drip is purpose-built for e-commerce email automation with deep revenue tracking, making it a better choice if your V0 app is a store or product-focused business.
Mailchimp offers a simpler API and more beginner-friendly setup, making it a better starting point if you just need a basic newsletter list without complex journey automation.
HubSpot Marketing Hub includes CRM, marketing automation, and analytics in one platform, making it more suitable if you need sales pipeline management alongside marketing journeys.
Frequently asked questions
Is Autopilot the same as Ortto?
Yes. Autopilot rebranded to Ortto in 2021. The product, features, and API are the same — only the name and branding changed. If you have an existing Autopilot account, you can log in at app.ortto.com with the same credentials. New accounts created after 2021 are under the Ortto brand.
Can I trigger a specific Ortto journey from my V0 app's API route?
Yes. Add a journey_id and trigger_id to your API payload when calling the person/merge endpoint, or use Ortto's separate journey trigger API. In your Ortto journey, the entry trigger must be set to 'API trigger' for programmatic enrollment to work. Find the trigger ID in the journey builder by clicking the entry block.
Does Ortto have a JavaScript SDK that V0 can generate code for?
Ortto has a JavaScript snippet for browser-side event tracking (similar to analytics.js), but there is no official Node.js SDK for server-side API calls. Your Next.js API routes will call the Ortto REST API directly using fetch(), which is what the code examples in this guide use.
How do I update an existing Ortto contact when a user changes their profile in my app?
Use the same POST /v1/person/merge endpoint with merge_by: ['str::email']. If a contact with that email already exists in Ortto, the API updates their record with the new field values. If no contact exists, it creates a new one. This upsert behavior means you can use the same endpoint for both creation and updates.
Can V0 generate the API route for Ortto automatically?
V0 can generate the structure of the API route and the fetch call when you describe the Ortto API payload format in your prompt. However, V0 may not know the exact Ortto field naming conventions (str::email, str::first) or your account's region URL. Provide these specifics in your prompt or review and correct the generated code against Ortto's API documentation.
What is the Ortto API rate limit?
Ortto's API rate limits vary by plan. On lower-tier plans, expect limits around 100-200 requests per minute. For high-traffic apps where many users sign up simultaneously, add a queue or batch your requests. For most V0 apps at early stage, the rate limits are not a practical concern.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation