Workspaces
Each workspace is an isolated mock API environment with its own subdomain, database, and configuration.
Overview
A workspace is the fundamental unit in Testfly. When you create a workspace, you get:
- A dedicated subdomain:
{slug}.mock.testfly.com - An isolated SQLite database with your mock data
- Full CRUD operations on every resource
- Configurable simulated conditions (latency, errors, CORS)
Workspaces are scoped to your organization. All team members with org access can manage workspaces.
curl -X POST https://api.testfly.com/api/workspaces \
-H "X-API-Key: tfk_your_key" \
-H "Content-Type: application/json" \
-d '{
"name": "E-Commerce API",
"slug": "ecommerce",
"rowCount": 1000,
"seedValue": 42
}'
Data generation
When you upload an OpenAPI spec, Testfly:
- Parses your spec — extracts schemas, relationships, and routes
- Emits .NET types at runtime with table and column attributes
- Creates SQLite tables with PK/FK constraints
- Generates data in topological order (parent tables first, then children)
Data generation uses Cufflink, a semantic fake data engine that understands column names and types. A column named email gets realistic email addresses. A column named price gets sensible decimal values. Foreign keys always point to real records in the referenced table.
Configuring row counts
The rowCount setting controls how many records are generated per resource table. Choose based on your use case:
| Row count | Use case | Generation time |
|---|---|---|
| 10 | Quick prototyping, UI development, demos | Instant |
| 100 (default) | General development, manual testing, API exploration | <1 second |
| 1,000 | Integration testing, pagination, filtering/sorting | A few seconds |
| 10,000 | Performance testing, realistic data volumes (Pro max) | ~10 seconds |
Set the row count when creating or updating a workspace:
# Create with 10,000 rows per table
curl -X POST .../api/workspaces \
-d '{ "name": "Load Test", "slug": "loadtest", "rowCount": 10000 }'
# Update to 1 million rows
curl -X PUT .../api/workspaces/{id} \
-d '{ "rowCount": 1000000 }'
# Regenerate data with the new count
curl -X POST .../api/workspaces/{id}/regenerate
After changing rowCount, call /regenerate to rebuild the database with the new volume.
Per-resource overrides
Some resources need different volumes. A realistic e-commerce mock might want 100 users, 50 products, and 10,000 orders. Per-resource rowCountOverride is available on the MockResource entity.
Reproducible data with seed values
Set a seedValue to get identical data every time:
curl -X POST .../api/workspaces \
-d '{ "name": "CI Mock", "slug": "ci-test", "rowCount": 100, "seedValue": 42 }'
This is essential for:
- CI/CD pipelines — assert against known data values
- Snapshot testing — compare mock responses deterministically
- Bug reproduction — recreate the exact dataset that triggered an issue
Same spec + same seed + same row count = identical mock API every time.
CRUD operations
Every resource gets full CRUD endpoints. All operations hit a real SQLite database — data persists until you regenerate or delete.
Pagination
GET /pets?page=3&pageSize=50
{
"data": [ ... ],
"meta": { "page": 3, "pageSize": 50, "totalCount": 10000, "totalPages": 200 }
}
Filtering
# All active cats
GET /pets?status=active&species=cat
Sorting
GET /pets?sortBy=name&sortDesc=true
Write operations
# Create — auto-generates UUID id
POST /pets { "name": "Buddy", "species": "dog" }
→ 201 Created + Location header
# Update — partial updates supported
PUT /pets/{id} { "name": "Buddy Jr" }
→ 200 OK
# Delete
DELETE /pets/{id}
→ 204 No Content
FK constraints are enforced — you can't create a pet with an owner_id that doesn't exist.
Simulated conditions
Configure workspace settings to test real-world conditions:
Latency
{ "settings": { "latencyMs": 500 } }
Every request delayed by 500ms. Test loading states, timeouts, and debouncing.
Error rate
{ "settings": { "errorRate": 0.1 } }
10% of requests return 500. Test retry logic and error handling.
CORS
{ "settings": { "corsOrigins": ["https://localhost:3000"] } }
Combined
PUT /api/workspaces/{id}
{
"settings": {
"latencyMs": 200,
"errorRate": 0.05,
"corsOrigins": ["https://app.example.com"],
"authEnabled": false
}
}
Workspace lifecycle
pause
↓
Provisioning → Active ↔ Paused
|
↓
Error Deleted
| Status | Mock API | Platform API | Transitions |
|---|---|---|---|
| Provisioning | 503 | Read-only | → Active or Error |
| Active | Live | Full access | Pause, Delete, Regenerate |
| Paused | 503 | Full access | Resume, Delete |
| Error | 503 | Read-only | Upload new spec to retry |
| Deleted | Gone | Gone | Terminal |
Regenerating data
POST /api/workspaces/{id}/regenerate
Drops all data, regenerates from the active spec. Use to reset between test runs, apply a new rowCount, or get fresh data after changing the spec.
Using workspaces in CI/CD
Testfly workspaces are designed for automation. A typical CI pipeline:
# 1. Create a workspace for this test run
WORKSPACE=$(curl -s -X POST .../api/workspaces \
-H "X-API-Key: $TESTFLY_KEY" \
-d "{
\"name\": \"CI Run $GITHUB_SHA\",
\"slug\": \"ci-$(echo $GITHUB_SHA | head -c 8)\",
\"rowCount\": 100,
\"seedValue\": 12345
}" | jq -r '.id')
# 2. Upload your spec
curl -X POST .../api/workspaces/$WORKSPACE/specs \
-H "X-API-Key: $TESTFLY_KEY" \
-d "{ \"rawSpec\": \"$(cat openapi.yaml)\", \"versionLabel\": \"ci\" }"
# 3. Wait for provisioning
sleep 5
# 4. Run integration tests against the mock
MOCK_URL="https://ci-$(echo $GITHUB_SHA | head -c 8).mock.testfly.com"
npm test -- --api-url=$MOCK_URL
# 5. Clean up
curl -X DELETE .../api/workspaces/$WORKSPACE \
-H "X-API-Key: $TESTFLY_KEY"
Use seedValue for deterministic assertions and rowCount to control data volume.
Ready to create your first workspace?
Start free. No credit card required.