1. Overview
Connect Upwork’s contract data to 1099Policy to ensure every freelancer engagement is insured or has a validated certificate of insurance (COI) before work begins. This integration automates policy binding, COI validation, and compliance updates.
By connecting Upwork and 1099Policy, you can:
- Bind fractional, per-contract insurance when a freelancer is hired
- Automatically validate uploaded COIs
- Sync policy and COI statuses through 1099Policy webhooks (
policy.*,certificate.*) - Map pay rates into
job.wage(in cents) andwage_type - Store Upwork IDs in
custom_metadatafor reconciliation
2. Integration Flow
Coverage should begin as soon as a contract is confirmed and before any work or payment starts, typically triggered when an offer is accepted in Upwork.
- Primary trigger:
offer.acceptedevent - Alternative trigger:
contract.active(if offers aren’t used) - Event delivery: HTTPS webhooks via Upwork’s Subscriptions API
- Required data: contract ID, freelancer ID, rate, and start/end dates
3. Core Concepts
1099Policy mirrors Upwork’s contract structure to determine who is covered and when. Each object in Upwork maps directly to a 1099Policy coverage concept.
| Upwork Object | 1099Policy Object | Description |
|---|---|---|
| Contract | Job + Assignment | Job defines role and pay; Assignment ties coverage to contract dates |
| Freelancer | Contractor | Represents the insured individual |
| Uploaded COI | Certificate | BYO-COI intake and validation |
| Contract ID | custom_metadata.engagement_id | Reconciliation key |
4. End-to-End Implementation
This section walks through the complete lifecycle, showing how to retrieve data from Upwork and create coverage records in 1099Policy.
4.1 Create Contractor
Each Upwork freelancer must exist as a Contractor in 1099Policy before a quote can be created and each Contractor must include a unique contact.email to ensure tenant-level uniqueness. If no email is provided by Upwork, generate a proxy such as <freelancer.id>@relay.yourdomain.com.
| Upwork Field | 1099Policy Contractor | Notes |
|---|---|---|
freelancer.id | custom_metadata.upwork_freelancer_id | Reconciliation key |
displayName | contact.first_name, contact.last_name | Split if possible |
location.country | address.country | Optional |
location.region / locality | address.region / address.city | Optional |
(Required) email | contact.email | Required; use proxy if missing |
Source: Upwork (GraphQL)
query FreelancerById($id: ID!) {
freelancer(id: $id) {
id
displayName
location { country region locality }
}
}
Destination: 1099Policy (POST /contractors)
POST https://api.1099policy.com/api/v1/contractors
Authorization: Bearer t9k_test_***
Content-Type: application/json
Idempotency-Key: upwork-freelancer-<FREELANCER_ID>
{
"contact": {
"first_name": "Ava",
"last_name": "Nguyen",
"email": "fl_12345@relay.yourdomain.com"
},
"address": {
"country": "US",
"region": "CA",
"city": "San Francisco"
},
"custom_metadata": {
"upwork_freelancer_id": "fl_12345"
}
}
4.2 Create Job
Jobs represent the scope, compensation, and category of the engagement. Create a Job when an Upwork offer is accepted or a contract is activated.
| 1099Policy Job Field | Upwork Source | Notes |
|---|---|---|
name | Contract title | Role name |
description | Contract description | Scope |
entity | Internal client/account ID | Must exist in 1099Policy |
category_code | Upwork category → mapped category | Maintain mapping table |
wage | hourlyRate.amount * 100 or fixedBudget.amount * 100 | Required |
wage_type | "hourly" or "flatfee" | Required |
region | Contractor or contract region | Defaults to contractor location |
custom_metadata.engagement_id | contract.id | Reconciliation |
Source: Upwork (GraphQL)
query ContractById($id: ID!) {
contract(id: $id) {
id
title
description
hourlyRate { amount }
fixedBudget { amount }
category { name }
}
}
Destination: 1099Policy (POST /jobs)
POST https://api.1099policy.com/api/v1/jobs
Authorization: Bearer t9k_test_***
Content-Type: application/json
{
"name": "Contract 9001 — Frontend Developer",
"description": "Build dashboard UI and integrate GraphQL APIs",
"entity": "en_abc123",
"category_code": "SOFTWARE_ENGINEERING_CONTRACT",
"wage": 7500,
"wage_type": "hourly",
"custom_metadata": { "engagement_id": "9001" }
}
4.3 Create Quote
Quotes determine the policy type and coverage duration for an engagement. Use the contractor, job, and contract start/end dates.
| 1099Policy Quote Field | Upwork Source | Notes |
|---|---|---|
contractor | Freelancer → Contractor mapping | Must exist first |
job | Returned Job ID | Required |
coverage_type[] | Compliance requirements | e.g., ["general"], ["professional"] |
effective_date | Contract start date (epoch UTC) | Required |
end_date | Contract end date (epoch UTC) | Required |
custom_metadata.engagement_id | contract.id | Reconciliation |
Source: Upwork (GraphQL)
query ContractDates($id: ID!) {
contract(id: $id) {
startDate
endDate
}
}
Destination: 1099Policy (POST /quotes)
POST https://api.1099policy.com/api/v1/quotes
Authorization: Bearer t9k_test_***
Content-Type: application/json
{
"contractor": "cn_FoA42zQ",
"job": "jb_Upw42Eng",
"coverage_type": ["general"],
"effective_date": 1764628800,
"end_date": 1772448000,
"custom_metadata": { "engagement_id": "9001" }
}
4.4 Create Insurance Application Session
The Application Session enables first-time contractors to review and bind coverage before work begins.
Destination: 1099Policy (POST /apply/sessions)
POST https://api.1099policy.com/api/v1/apply/sessions
Authorization: Bearer t9k_test_***
Content-Type: application/json
{
"quote": "qt_Zk39A2L",
"success_url": "https://your.app/contracts/9001?coverage=active",
"cancel_url": "https://your.app/contracts/9001?coverage=canceled",
"custom_metadata": { "engagement_id": "9001" }
}
Contractors complete this step once to activate their first policy.
4.5 Create Assignment
Assignments apply coverage automatically for returning contractors who have already opted in.
Destination: 1099Policy (POST /assignments)
POST https://api.1099policy.com/api/v1/assignments
Authorization: Bearer t9k_test_***
Content-Type: application/json
{
"contractor": "cn_FoA42zQ",
"job": "jb_Upw42Eng",
"effective_date": 1764628800,
"end_date": 1772448000,
"coverage_type": ["general"],
"custom_metadata": { "engagement_id": "9001" }
}
4.6 Upload Certificate (BYO-COI)
If a contractor provides their own COI, upload it to 1099Policy for validation.
Source: Upwork (GraphQL)
query ContractAttachments($id: ID!) {
contract(id: $id) {
id
attachments {
id
fileName
url
contentType
}
}
}
Destination: 1099Policy (POST /files/certificates)
(multipart upload)
Fields:
certificate=@file.pdfcontractor=cn_FoA42zQcustom_metadata[engagement_id]=9001
4.7 Record Invoice (Optional)
If the contractor’s final pay differs from the original estimate, record the actual remuneration for reconciliation and audit. This step is optional and does not affect insurance coverage.
Destination: 1099Policy (POST /invoices)
POST https://api.1099policy.com/api/v1/invoices
Authorization: Bearer t9k_test_***
Content-Type: application/json
{
"contractor": "cn_ti8eXviE4A",
"job": "jb_rajdrwMUKi",
"gross_pay": 1200,
"paycycle_startdate": 1714419793,
"paycycle_enddate": 1714419793
}
Use Unix seconds (UTC) for paycycle_startdate and paycycle_enddate. See full API reference: https://docs.1099policy.com/group/endpoint-invoice
5. Webhooks
Webhooks keep both systems synchronized as coverage and certificates change.
| Source | Event | Action |
|---|---|---|
| Upwork | offer.accepted | Create Job and Quote |
| Upwork | contract.active | Create Assignment |
| Upwork | contract.ended | End coverage |
| 1099Policy | policy.active | Mark contractor insured |
| 1099Policy | policy.expired / policy.canceled | Mark coverage inactive |
| 1099Policy | certificate.validated | Mark COI valid |
| 1099Policy | certificate.flagged | Flag for review |
6. Testing Checklist
Use this checklist to confirm your integration behaves as expected.
- Contractor created with valid proxy email if needed
- Job created with wage and metadata
- Quote created successfully with correct dates
- Application Session completes and binds coverage
- Assignment aligns with contract window
- Webhook endpoint registered and verified
- Certificate upload validated

