A comprehensive Next.js application for managing WhatsApp AI agents via the BerryLabs API. This platform provides a complete dashboard for creating, configuring, and managing intelligent WhatsApp chatbots with advanced features like knowledge bases, file references, webhook integrations, and scheduling.
π Try Live Demo - Experience the platform in action!
- Create & Configure Agents: Set up AI agents with custom names, descriptions, and system prompts
- Multi-language Support: English and Indonesian language options
- Agent Dashboard: View and manage all your agents in a comprehensive table
- Delete Protection: Confirmation dialogs prevent accidental deletions
- Document Upload: Support for PDF, DOCX, and other document formats
- Text Content: Add raw text content directly to the knowledge base
- Smart Library: Centralized document library with add/remove functionality
- Auto-Assignment: Uploaded documents automatically added to agent's knowledge base
- Status Tracking: Monitor document processing status (completed, processing, error)
- File Upload: Upload images, videos, audio, and documents for agent responses
- Reference Codes: Automatic generation of file reference codes (e.g., file-A, file-B)
- Usage Instructions: Built-in examples showing how to use file references in prompts
- Smart Management: Add files from library or upload new ones directly
- Complete Configuration: Full webhook setup with tabbed interface
- HTTP Methods: Support for GET, POST, PUT, PATCH, DELETE
- Parameter Management: Configure headers, path params, query params, and body params
- Tool Testing: Built-in testing functionality to validate webhook endpoints
- Dynamic Variables: Support for LLM prompts and dynamic variables in parameters
- QR Code Generation: Easy WhatsApp account connection via QR scanning
- Multiple Accounts: Support for multiple WhatsApp accounts per user
- Real-time Status: WebSocket integration for live connection status updates
- Account Management: Connect, disconnect, and reconnect accounts with confirmation
- Association Control: Link specific WhatsApp accounts to individual agents
- Operating Hours: Configure daily operating schedules for each agent
- Timezone Support: Automatic timezone detection with manual override
- Always Active Option: 24/7 operation mode available
- Day-specific Settings: Individual schedules for each day of the week
- AI Behavior: Configure manual takeover and auto-reset timings
- Data Collection: Custom fields for collecting specific information from users
- Field Management: Add, edit, and remove data collection fields with validation
- Frontend: Next.js 15.5.2 with App Router
- Styling: Tailwind CSS 4.1.13
- UI Components: Shadcn UI with Radix primitives
- Icons: Lucide React 0.542.0
- HTTP Client: Axios 1.11.0
- Form Validation: Zod schemas
- Notifications: Sonner toast library
- Package Manager: pnpm
- Node.js 18.0 or higher
- pnpm (recommended) or npm
- BerryLabs account and API key
- Clerk account for authentication
- Prisma Postgres account (get from console.prisma.io)
# Clone the repository
git clone <repository-url>
cd whatsapp-ai
# Install dependencies using pnpm (recommended)
pnpm install
# or using npm
npm install
This application uses Prisma Postgres for database management.
-
Sign up for Prisma Data Platform
- Go to console.prisma.io
- Create a free account or log in
-
Create a New Project
- Click "New Project" in the Prisma Console
- Select "Prisma Postgres" as your database provider
- Choose your preferred region
-
Get Database Connection String
- After creating the project, Prisma will provide your connection string
- Copy the
DATABASE_URL
- it will look like:prisma://accelerate.prisma-data.net/?api_key=...
Create a .env.local
file in the project root directory and configure all required environment variables:
# =================================
# DATABASE CONFIGURATION
# =================================
# Prisma Postgres Database URL - Get this from console.prisma.io
# Format: prisma://accelerate.prisma-data.net/?api_key=your_api_key
PRISMA_DATABASE_URL="prisma://accelerate.prisma-data.net/?api_key=your_prisma_api_key"
# =================================
# CLERK AUTHENTICATION
# =================================
# Get these from https://dashboard.clerk.com
# Navigate to: Dashboard β Your App β API Keys
# Clerk Publishable Key (starts with pk_test_ or pk_live_)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_your-publishable-key-here"
# Clerk Secret Key (starts with sk_test_ or sk_live_)
CLERK_SECRET_KEY="sk_test_your-secret-key-here"
# Clerk URL Configuration
NEXT_PUBLIC_CLERK_SIGN_IN_URL="/sign-in"
NEXT_PUBLIC_CLERK_SIGN_UP_URL="/sign-up"
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL="/dashboard"
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL="/dashboard"
# =================================
# BERRYLABS API CONFIGURATION
# =================================
# BerryLabs API Base URL
NEXT_PUBLIC_BERRYLABS_API_URL="https://api.berrylabs.io"
# BerryLabs API Key - Get from https://app.berrylabs.io
# Navigate to: User Avatar β API Keys β Generate API Key
NEXT_PUBLIC_BERRYLABS_API_KEY="your-berrylabs-api-key-here"
# =================================
# OPTIONAL: DEVELOPMENT SETTINGS
# =================================
# Next.js Development Mode
NODE_ENV="development"
# Enable Prisma Debug Logging (optional)
# DEBUG="prisma:query"
Variable | Required | Description | Example |
---|---|---|---|
PRISMA_DATABASE_URL |
β | Prisma Postgres connection string | prisma://accelerate.prisma-data.net/?api_key=... |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY |
β | Clerk frontend authentication key | pk_test_... |
CLERK_SECRET_KEY |
β | Clerk backend secret key | sk_test_... |
NEXT_PUBLIC_BERRYLABS_API_URL |
β | BerryLabs API base URL | https://api.berrylabs.io |
NEXT_PUBLIC_BERRYLABS_API_KEY |
β | Your BerryLabs API key | your-api-key |
NODE_ENV |
β | Development environment | development |
- Never commit
.env.local
to version control - Use different API keys for development and production
- Restrict database access to necessary IP addresses only
- Use strong passwords for database connections
- Rotate API keys regularly for better security
BerryLabs supports two types of authentication based on your account type:
- Standard Users: Use
xi-api-key
only (for personal use) - Reseller Partners: Use both
xi-api-key
andxi-client-key
(for managing client accounts)
Choose the appropriate guide below based on your use case:
If you're using BerryLabs for your own WhatsApp AI agents, follow these steps:
Step 1: Register an Account
- Go to app.berrylabs.io/auth/register
- Complete the registration process with your details
- Verify your email address if required
Step 2: Access API Keys
- Log in to the BerryLabs platform at app.berrylabs.io
- Click on your user avatar at the bottom left of the interface
- A modal will appear - select "API Keys" from the options
Step 3: Generate API Key
- In the API Keys section, click "Generate API Key"
- Give your API key a descriptive name (e.g., "Production API", "WhatsApp AI Development")
- Important: Copy the generated API key immediately
- Add it to your
.env.local
file asNEXT_PUBLIC_BERRYLABS_API_KEY
- Store it securely as it won't be shown again
- Save your API key immediately after generation
- The key will only be displayed once for security reasons
- If you lose your key, you'll need to generate a new one
Example .env.local
configuration:
NEXT_PUBLIC_BERRYLABS_API_KEY="your-standard-api-key-here"
π Learn More: Standard User Authentication Guide
If you're a reseller managing multiple client accounts or building a platform to resell BerryLabs services, follow these steps:
Overview
Reseller authentication enables you to manage multiple end clients through a single reseller account. There are two main use cases:
- Client Registration & Subscription Management - Only requires
xi-api-key
- Managing Existing Client Resources - Requires both
xi-api-key
andxi-client-key
Step 1: Apply for Reseller Status
- Visit the BerryLabs Partner Program Application
- Fill out the partnership application form (choose Business or Individual partnership)
- Submit your application and complete the verification process
- Wait for approval confirmation from the BerryLabs team
- You'll receive notification when your reseller status is approved
Step 2: Access Reseller Dashboard
- Log in to app.berrylabs.io
- Click on the navbar to find the "Reseller Dashboard" switcher
- Switch to your approved Reseller Dashboard
- You'll be redirected to the reseller-specific interface
Step 3: Get Your Reseller API Key
- In the Reseller Dashboard, click on the "Settings" menu
- Select "API Keys" from the submenu options
- Your reseller API key will be displayed (or generate one if needed)
- Copy the reseller API key
- Add it to your
.env.local
file asNEXT_PUBLIC_BERRYLABS_API_KEY
Step 4: Understanding Client Keys
Reseller API authentication varies by endpoint type:
For Registration/Subscription Endpoints (No client key needed):
POST /api/reseller/client/register
POST /api/reseller/client
POST /api/reseller/client/subscription
POST /api/reseller/client/client-key
GET /api/reseller/client/packages
GET /api/reseller/client/order/{orderId}/status
For Resource Management Endpoints (Client key required):
- All agent management endpoints (
/v1/wa/agents/*
) - All WhatsApp account endpoints (
/v1/wa/accounts/*
) - All knowledge base endpoints (
/v1/wa/knowledge/*
)
Example .env.local
configuration:
# Your Reseller API Key (for all operations)
NEXT_PUBLIC_BERRYLABS_API_KEY="your-reseller-api-key-here"
# Client keys are obtained dynamically per client and stored in cookies/database
# This app automatically manages client keys through the authentication flow
How This App Handles Reseller Authentication:
This application automatically handles both reseller authentication scenarios:
- During Registration/Subscription: Uses only the reseller API key
- During Resource Management: Automatically includes the client key from cookies when available
- Client Key Storage: Client keys are stored in cookies after successful payment and retrieved automatically
Getting Client Keys After Registration:
After a client completes payment through your reseller account:
- Use the
access_id
from the payment callback - Call
POST /api/reseller/client/client-key
with theaccess_id
- Store the returned
client_key
securely in your system - This app stores it in cookies automatically using
setClientKeyCookie()
π Important Reseller Notes:
- Reseller API keys have different permissions than standard user keys
- Client keys are required for managing specific client resources
- Follow a separate application-based approval process (not self-service)
- Each client you register will have their own unique
xi-client-key
π Learn More: Reseller Authentication Guide
This application is designed to work seamlessly for both user types:
For Standard Users:
- API key is set in
.env.local
and used for all requests - No client key management needed
- Direct access to personal agents and resources
For Reseller Partners:
- Reseller API key is set in
.env.local
- Client keys are managed automatically through the authentication flow
- The app detects missing client keys and shows helpful banners with partnership information
- The subscription page shows reseller partnership opportunities for unauthorized users
Automatic Client Key Management:
The application includes built-in client key management:
getClientKeyFromCookie()
- Retrieves stored client keysetClientKeyCookie()
- Stores client key after successful paymentremoveClientKeyCookie()
- Clears client key on logout- Axios interceptors automatically include client keys in API requests
-
Create Clerk Account
- Go to dashboard.clerk.com
- Sign up for a free account
- Create a new application
-
Get API Keys
- In your Clerk dashboard, go to API Keys
- Copy the Publishable Key and Secret Key
- Add both keys to your
.env.local
file
-
Configure Authentication URLs
- The authentication URLs are already configured in the example above
- These routes (
/sign-in
,/sign-up
,/dashboard
) must match your application structure
Once your environment variables are configured, set up the database schema:
# Generate Prisma client
pnpm prisma generate
# or
npx prisma generate
# Run database migrations to create tables (development only)
pnpm prisma migrate dev --name init
# or
npx prisma migrate dev --name init
# (Optional) Seed database with initial data
pnpm prisma db seed
# or
npx prisma db seed
# (Optional) Open Prisma Studio to view/edit data
pnpm prisma studio
# or
npx prisma studio
# Generate Prisma client
pnpm prisma generate
# Deploy migrations to production (use this in production, NOT migrate dev)
pnpm prisma migrate deploy
# or
npx prisma migrate deploy
Important:
- Use
prisma migrate dev
only in development - Use
prisma migrate deploy
for production deployments - Never run
prisma migrate dev
in production as it may cause data loss
The application creates these main tables:
- users: Local user data with Clerk integration
- agents: WhatsApp AI agent configurations
- subscriptions: User subscription records
- orders: Payment and order tracking
# View migration status
pnpm prisma migrate status
# Reset database (WARNING: deletes all data - development only)
pnpm prisma migrate reset
# Create a new migration after schema changes (development only)
pnpm prisma migrate dev --name your-migration-name
Test your configuration:
# Check database connection
pnpm prisma db pull
# Validate environment variables
pnpm dev
# Check if all services are running
echo "Visit http://localhost:3000 to see your application"
# Start the development server
pnpm dev
# or
npm run dev
Open http://localhost:3000 in your browser.
- API Headers: Automatically added to all BerryLabs API calls
- Frontend Auth: Clerk - Complete user authentication solution
- Database: Prisma - Type-safe database ORM
- External API: BerryLabs API - WhatsApp AI agent management
- Session Management: Cookie-based client key storage for API access
The application uses a dual-database architecture:
model User {
id String @id @default(cuid())
clerkId String @unique
email String
firstName String
lastName String
phone String?
clientKey String? // BerryLabs API client key
berryLabsUserId String? // BerryLabs user ID
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
subscriptions Subscription[]
}
model Subscription {
id String @id @default(cuid())
userId String
packageId String
subscriptionId String @unique
packageName String
status String
totalAmount String
subsType String?
organizationId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
orders Order[]
}
model Order {
id String @id @default(cuid())
userId String
subscriptionId String?
orderId String @unique
amount String
status String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
subscription Subscription? @relation(fields: [subscriptionId], references: [id])
}
graph TD
A[User visits /sign-in] --> B[Clerk Authentication]
B --> C[Email/Password Login]
C --> D[Clerk Session Created]
D --> E[Redirect to /dashboard]
E --> F[Check User in Local DB]
F --> G[Access Dashboard Content]
Implementation:
- Uses Clerk's built-in sign-in component
- Automatic session management
- Protected routes via Clerk middleware
graph TD
A[User visits /sign-up] --> B[Fill Registration Form]
B --> C[Clerk Account Creation]
C --> D[Email Verification]
D --> E[Save User to Local DB]
E --> F[Register with BerryLabs API]
F --> G[Store Client Key]
G --> H[Redirect to /dashboard]
Implementation:
// src/components/custom-signup.tsx
const handleVerification = async () => {
// 1. Complete Clerk verification
const completeSignUp = await signUp.attemptEmailAddressVerification({
code: verificationCode,
});
// 2. Save to local database
await createUserInDatabase({
clerkId: completeSignUp.createdUserId!,
email: formData.email,
firstName: formData.firstName,
lastName: formData.lastName,
phone: formData.phone,
});
// 3. Register with BerryLabs API
const apiResponse = await clientApi.register({
name: `${formData.firstName} ${formData.lastName}`,
email: formData.email,
phone: formData.phone,
});
// 4. Store client key
setClientKeyCookie(apiResponse.data.client_key);
await updateUserWithBerryLabsData(userId, apiResponse.data);
};
graph TD
A[User visits /register-subscribe] --> B[Fill Form + Select Package]
B --> C[Clerk Account Creation]
C --> D[Email Verification]
D --> E[Auto-redirect to /register-subscribe-complete]
E --> F[Save User to Local DB]
F --> G[Call BerryLabs /api/reseller/client]
G --> H[Redirect to Payment Gateway]
H --> I[Payment Completion]
I --> J[Return to /payment-status?access_id=xxx]
J --> K[Get Client Key from access_id]
K --> L[Update User with Client Key]
L --> M[Store Client Key in Cookies]
M --> N[Check Payment Status]
N --> O[Redirect to /dashboard]
Key Implementation Details:
Step 1: Registration with Package Selection
// src/app/register-subscribe/page.tsx
const handleSubmit = async (e: React.FormEvent) => {
// Standard Clerk registration
await signUp.create({
emailAddress: formData.email,
password: formData.password,
unsafeMetadata: {
firstName: formData.firstName,
lastName: formData.lastName,
phone: formData.phone,
},
});
// Store package data for later
localStorage.setItem(
"postRegistrationSubscription",
JSON.stringify({
packageId: packageId,
billingCycle: billingCycle,
userEmail: formData.email,
userPhone: formData.phone,
userName: `${formData.firstName} ${formData.lastName}`,
})
);
// Redirect to email verification
router.push("/verify-email?redirect=/register-subscribe-complete");
};
Step 2: Automatic Subscription Creation
// src/app/register-subscribe-complete/page.tsx
const handleCreateAccount = async () => {
// 1. Create user in local database
await createUserInDatabase({
clerkId: user.id,
email: user.primaryEmailAddress?.emailAddress,
firstName: user.unsafeMetadata?.firstName,
lastName: user.unsafeMetadata?.lastName,
phone: user.unsafeMetadata?.phone,
});
// 2. Call BerryLabs API to register + create subscription
const response = await clientApi.createSubscriptionRegister({
name: subscriptionData.userName,
phone: subscriptionData.userPhone,
email: subscriptionData.userEmail,
package_id: subscriptionData.packageId,
sub_type: subscriptionData.billingCycle,
});
// 3. Redirect to payment
window.location.href = response.data.url;
};
Step 3: Payment Status with Client Key Setup
// src/app/payment-status/page.tsx
useEffect(() => {
const setupClientKeyFromAccessId = async () => {
if (!accessId || !user) return;
// Get client key from access_id
const response = await clientApi.getClientKey(accessId);
if (response?.data?.client_key) {
// Set client key in cookies
setClientKeyCookie(response.data.client_key);
// Update user's clientKey in database
await updateUserClientKey(
user.id,
response.data.client_key,
response.data.user_id
);
// Mark setup as complete
setClientKeySetupComplete(true);
}
};
setupClientKeyFromAccessId();
}, [accessId, user]);
// Only check payment status after client key setup
useEffect(() => {
if (orderId && (!accessId || clientKeySetupComplete)) {
checkPaymentStatus();
}
}, [orderId, accessId, clientKeySetupComplete]);
The client key is crucial for accessing BerryLabs API endpoints and must be properly managed:
- Database:
users.clientKey
field for persistence - Cookies:
xi-client-key
for API requests - API Headers: Automatically added to all BerryLabs API calls
// src/lib/api.ts
api.interceptors.request.use((config) => {
const clientKey = getClientKeyFromCookie();
const apiKey = process.env.NEXT_PUBLIC_BERRYLABS_API_KEY;
if (apiKey) {
config.headers["xi-api-key"] = apiKey;
}
if (clientKey) {
config.headers["xi-client-key"] = clientKey;
}
return config;
});
Dashboard access requires both Clerk authentication and a valid client key:
// Dashboard protection logic
const { user } = useUser();
const [hasClientKey, setHasClientKey] = useState(false);
useEffect(() => {
const checkClientKey = async () => {
if (user) {
const clientKey = getClientKeyFromCookie();
if (!clientKey) {
// Redirect to registration or setup
router.push("/setup-account");
} else {
setHasClientKey(true);
}
}
};
checkClientKey();
}, [user]);
POST /api/users/create
- Create user in local databasePOST /api/users/update-client-key
- Update user's client keyPOST /api/users/update-berrylabs-data
- Update BerryLabs user data
POST /api/subscriptions/create
- Create subscription recordPOST /api/orders/save
- Save order information
POST /api/reseller/client/register
- Register user onlyPOST /api/reseller/client
- Register user + create subscriptionPOST /api/reseller/client/client-key
- Get client key from access_idGET /api/reseller/client/order/{orderId}/status
- Check payment status
Database Connection Issues:
# Test database connection
pnpm prisma db pull
Clerk Authentication Issues:
- Verify API keys are correct
- Check that domain matches in Clerk dashboard
- Ensure authentication URLs are properly configured
BerryLabs API Issues:
- Verify API key is active
- Check API key permissions
- Test API connection with a simple curl request:
curl -X GET https://api.berrylabs.io/v1/wa/agents \
-H "xi-api-key: your-api-key" \
-H "Content-Type: application/json"
Build or Runtime Errors:
# Clear Next.js cache
rm -rf .next
# Reinstall dependencies
rm -rf node_modules package-lock.json
pnpm install
# Regenerate Prisma client
pnpm prisma generate
This project is free and open source for personal and internal business use. You can:
- Use it for your own WhatsApp AI agents
- Modify the code to suit your needs
- Deploy it for your organization's internal use
If you plan to resell this solution or manage multiple clients, you'll need:
π Complete Partnership Information: BerryLabs Partnership Program
- Reseller API Key: Required for managing multiple client accounts
- Client Key: Needed for end-client management via BerryLabs Partnership Program
- Authentication: Follow the Reseller Authentication Guide
- Attractive Revenue Sharing: Up to 60% revenue share for qualified resellers
- Access to reseller API endpoints
- Client management capabilities
- Partner support and resources
- Comprehensive partner training and onboarding
- Keep BerryLabs Branding: The "Powered by BerryLabs" branding must remain visible
- Attribution Required: Do not remove existing BerryLabs brand elements
- Whitelabel Options: Available for qualified partners
For custom branding and whitelabel solutions:
- Contact: [email protected]
- Subject: Partnership Whitelabel Program
- Include: Your use case and partnership requirements
- β Free Use: Personal and internal business use
- β Modifications: Code customization allowed
- β Reselling: Requires partnership agreement and reseller keys
- β Brand Removal: BerryLabs attribution must remain
- πΌ Commercial: Whitelabel options available through partnership
- Access Dashboard: Navigate to the main page to see your agents table
- Create Agent: Click "Create Agent" button in the top right
- Configure Basic Info:
- Set agent name and description
- Choose language (English/Indonesian)
- Write comprehensive system prompt
- Navigate to Agent Details: Click on any agent from the table
- Go to Agent Tab: The first tab contains knowledge base configuration
- Add Documents:
- Upload File: Click "Upload File" for PDF, DOCX, etc.
- Add Text: Click "Add Text" for raw text content
- From Library: Use "Add from Library" for previously uploaded documents
- Upload Files: Use "Upload File" in the File References section
- Reference in Prompts: Use the generated file codes in your system prompt
Example: "When client asks about menu, send file-A"
- View Instructions: Built-in examples show proper usage patterns
- Add Tool: Click "Add Tool" in the Tools section
- Configure Endpoint:
- Set tool name and description
- Enter webhook URL and HTTP method
- Configure Parameters:
- Headers: Add authentication headers
- Path Params: Define URL path parameters
- Query Params: Set URL query parameters
- Body: Configure request body for POST/PUT/PATCH
- Test Tool: Use built-in testing to validate configuration
- Go to WhatsApp Tab: Click the WhatsApp tab in agent details
- Generate QR Code: Click "Connect New Account"
- Scan QR: Use WhatsApp mobile app to scan the displayed QR code
- Associate Account: Toggle the switch to associate account with agent
- Navigate to Scheduling Tab: Configure operating hours
- Set Mode:
- Always Active: 24/7 operation
- Scheduled: Custom hours per day
- Configure Daily Hours: Set open/close times for each day
- Select Timezone: Choose appropriate timezone
- Complete API Docs: BerryLabs API Documentation
- Authentication Guide: API Key Authentication
All API requests require authentication via header:
xi-api-key: your-api-key
Content-Type: application/json
Example request:
curl -X GET https://api.berrylabs.io/v1/wa/agents \
-H "xi-api-key: your-api-key" \
-H "Content-Type: application/json"
- Toast Notifications: Success/error feedback for all operations
- Auto-scroll: Automatic scrolling to relevant sections when needed
- Floating Save CTA: Context-aware save button appears when changes are made
- Loading States: Visual feedback during API operations
- Responsive Design: Works on desktop and mobile devices
- Confirmation Dialogs: Prevent accidental data loss
src/
βββ app/ # Next.js app router pages
β βββ agent/[id]/ # Agent detail page
β βββ page.tsx # Main dashboard
βββ components/ # Reusable UI components
β βββ agent-tabs/ # Agent configuration tabs
β βββ ui/ # Shadcn UI components
β βββ modals/ # Upload and configuration modals
βββ lib/ # Utilities and API configuration
β βββ api.ts # BerryLabs API client
β βββ utils.ts # Helper functions
βββ schemas/ # Zod validation schemas
β βββ agent-schema.ts # Agent data validation
βββ types/ # TypeScript type definitions
βββ agent.ts # Agent-related types
βββ files.ts # File management types
βββ knowledge-base.ts # Knowledge base types
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
- API Issues: Check BerryLabs Documentation
- Technical Support: Contact BerryLabs support team
- Project Issues: Create an issue in this repository
Powered by BerryLabs π - Visit BerryLabs