---
title: Create a certificate
description: Create a new certificate in a workspace
method: POST
path: /api/v1/workspaces/{slug}/certificates
group: Certificates
scopes: certificates:write
---

# Create a certificate

# Create a certificate

Creates a certificate (defaults to `draft`) and an owned template draft. `name` is required.

## Authorization

```http
Authorization: Bearer vc_...
```

Requires `certificates:write` (or full access) and an active Pro seat. Enabling request or correction forms also requires an active seat for operate features.

## Path parameters

  
- `slug*` string — Workspace slug Example: `acme`.

## Body parameters

  
- `name*` string — Certificate name Example: `Python Fundamentals`.

  
- `status?` string — `draft`, `active`, or `archived` (defaults to `draft`) Example: `active`.

  
- `description?` string — What this certificate represents (max 4000 chars) Example: `Completion of the Python fundamentals track`.

  
- `certificate_type?` string — One of `certificate`, `badge`, `certification`, `course`, `workshop`, `event`, `membership`, `recognition` Example: `course`.

  
- `level?` string — One of `beginner`, `intermediate`, `advanced`, `professional` Example: `beginner`.

  
- `duration?` string — Free-text time to earn (max 80 chars) Example: `8 weeks`.

  
- `learn_more_url?` string — `http` or `https` URL Example: `https://example.com/python`.

  
- `earning_criteria?` string[] — Up to 10 criteria, each ≤ 500 chars Example: `["Complete all modules"]`.

  
- `skill_ids?` UUID[] — Ordered skill ids to link (max 20)

  
- `request_form_enabled?` boolean — Enable the public certificate request form Example: `true`.

  
- `request_form_mode?` string — `auto_issue_all` issues every applicant on submit; `manual_approval` queues each one for review. Defaults to `manual_approval`. Example: `manual_approval`.

  
- `unlock_form_enabled?` boolean — Hold imported recipients behind an unlock form until they respond Example: `true`.

  
- `correction_form_enabled?` boolean — Enable correction form Example: `true`.

  
- `correction_form_mode?` string — Defaults to `manual_approval`

Attribute definitions, form fields, and `validity_days` are set via update after create when needed.

## Request

```bash
curl -X POST https://api.verifycate.com/api/v1/workspaces/acme/certificates \
  -H "Authorization: Bearer vc_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Python Fundamentals",
    "description": "Completion of the Python fundamentals track",
    "certificate_type": "course",
    "level": "beginner"
  }'
```

```javascript
const res = await fetch(
  "https://api.verifycate.com/api/v1/workspaces/acme/certificates",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer vc_your_api_key",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "Python Fundamentals",
      description: "Completion of the Python fundamentals track",
      certificate_type: "course",
      level: "beginner",
    }),
  },
);
const certificate = await res.json();
```

```go
body := strings.NewReader(`{
  "name": "Python Fundamentals",
  "certificate_type": "course",
  "level": "beginner"
}`)
req, _ := http.NewRequest(
	"POST",
	"https://api.verifycate.com/api/v1/workspaces/acme/certificates",
	body,
)
req.Header.Set("Authorization", "Bearer vc_your_api_key")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
```

```python
import json
import urllib.request

payload = json.dumps({
    "name": "Python Fundamentals",
    "certificate_type": "course",
    "level": "beginner",
}).encode()
req = urllib.request.Request(
    "https://api.verifycate.com/api/v1/workspaces/acme/certificates",
    data=payload,
    method="POST",
    headers={
        "Authorization": "Bearer vc_your_api_key",
        "Content-Type": "application/json",
    },
)
with urllib.request.urlopen(req) as res:
    print(res.read().decode())
```

## Response schema

- `active_recipient_count*` integer — 
- `attributes*` object — 
- `blockchain_enabled*` boolean — 
- `certificate_type*` string — 
- `correction_form_enabled*` boolean — 
- `correction_form_fields*` array — 
- `correction_form_mode*` string — 
- `correction_form_token*` string — 
- `created_at*` string — 
- `description*` string — 
- `duration*` string — 
- `earning_criteria*` object — 
- `has_template*` boolean — 
- `id*` string — 
- `learn_more_url*` string — 
- `level*` string — 
- `name*` string — 
- `pending_recipient_count*` integer — 
- `published_template_version?` integer — 
- `recipient_status_counts?` RecipientStatusCounts — 
  - `awaiting_form_response*` integer — 
  - `awaiting_review*` integer — 
  - `changes_requested*` integer — 
  - `expired*` integer — 
  - `issued*` integer — 
  - `ready_to_send*` integer — 
  - `waiting_for_publish*` integer — 
- `request_form_enabled*` boolean — 
- `request_form_fields*` array — 
- `request_form_mode*` string — 
- `request_form_token*` string — 
- `skills*` array — 
- `sponsors*` array — 
- `status*` string — 
- `template_versions?` array — 
- `unlock_form_enabled*` boolean — 
- `unlock_form_fields*` array — 
- `unlock_release_policy?` string — 
- `updated_at*` string — 
- `validity_days*` integer — 
- `workspace_id*` string — 

## Response

### 200

Created certificate object (same shape as [Get a certificate](/docs/api/certificates/get)).

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "workspace_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "name": "Python Fundamentals",
  "status": "draft",
  "description": "Completion of the Python fundamentals track",
  "certificate_type": "course",
  "level": "beginner",
  "skills": [],
  "has_template": true,
  "pending_recipient_count": 0,
  "active_recipient_count": 0,
  "created_at": "2026-09-03T08:00:00Z",
  "updated_at": "2026-09-03T08:00:00Z"
}
```

### 400

```json
{
  "title": "Bad Request",
  "status": 400,
  "detail": "certificate name is required",
  "code": "name_required"
}
```
