---
title: "Manage Contacts with the FirstSales CLI | FirstSales"
description: "CRUD contacts, lists, and tags from the command line — idempotent creates, bulk imports, exports, and deliberate deletes scoped to an org and workspace."
canonical: "https://firstsales.io/tutorial/manage-contacts-via-cli/"
---

[Home](/)/[Tutorials](/tutorial/)/Manage Contacts with the FirstSales CLI

Developer & CLI

# Manage Contacts with the FirstSales CLI

CRUD contacts, lists, and tags from the command line — idempotent creates, bulk imports, exports, and deliberate deletes scoped to an org and workspace.

8 min read·Advanced·7 steps

1. 1  
## List and read contacts  
Everything is scoped to an org and workspace. Start read-only — list contacts, then fetch one by ID:  
```  
firstsales contacts list --org ORG_ID --workspace WS_ID --json  
firstsales contacts get --org ORG_ID --workspace WS_ID --contact CONTACT_ID  
```
2. 2  
## Create and update a contact  
Create with an idempotency key so retries don't duplicate; update patches only the fields you send:  
```  
firstsales contacts create --org ORG_ID --workspace WS_ID \  
  --data '{"email":"jane@acme.com","first_name":"Jane","company_name":"Acme"}' \  
  --idempotency-key jane-acme-2026-01  
firstsales contacts update --org ORG_ID --workspace WS_ID \  
  --contact CONTACT_ID --data '{"job_title":"VP Sales"}'  
```
3. 3  
## Organize with lists  
Contact lists are how you segment who a campaign sends to. Create, rename, and delete lists from the CLI:  
```  
firstsales contact-lists list --org ORG_ID --workspace WS_ID  
firstsales contact-lists create --org ORG_ID --workspace WS_ID \  
  --data '{"name":"Q1 SaaS founders"}'  
firstsales contact-lists update --org ORG_ID --workspace WS_ID \  
  --list LIST_ID --data '{"name":"Q1 SaaS founders (US)"}'  
```
4. 4  
## Manage tags in bulk  
Tags are free-form labels across contacts. You can rename (which merges) or delete a tag everywhere it appears — deletion is destructive, so it needs `--confirm`:  
```  
firstsales contact-tags list --org ORG_ID --workspace WS_ID  
firstsales contact-tags rename --org ORG_ID --workspace WS_ID \  
  --data '{"from":"founder","to":"founders"}'  
firstsales contact-tags delete --org ORG_ID --workspace WS_ID \  
  --data '{"tag":"stale"}' --confirm  
```
5. 5  
## Bulk import  
Push a batch of contacts with an import job. Point it at a list; the platform de-duplicates by email server-side, so re-importing the same people won't create copies:  
```  
firstsales contact-imports create --org ORG_ID --workspace WS_ID \  
  --data-file ./leads.json \  
  --idempotency-key import-2026-01  
```
6. 6  
## Export what you filtered  
List your export jobs / pull contacts back out as CSV for reporting or backup:  
```  
firstsales contact-exports list --org ORG_ID --workspace WS_ID  
```
7. 7  
## Delete deliberately  
Removing a contact is destructive and requires `--confirm`. Prefer letting cleaning auto-suppress bad addresses over hard-deleting — deletion is permanent:  
```  
firstsales contacts delete --org ORG_ID --workspace WS_ID \  
  --contact CONTACT_ID --confirm  
```

## Pro tips

Hard-won shortcuts that keep warm-up on track.

1

### Segment with lists, label with tags

Lists decide who a campaign sends to; tags are cross-cutting labels for filtering. Use both — a contact can sit in one list and carry many tags.

2

### Import is idempotent and de-duped

Server-side dedup by email means a re-run of an import won't double your list. Still pass --idempotency-key so the job itself isn't created twice.

3

### Don't hard-delete to suppress

To stop emailing someone, let cleaning mark them dirty or rely on bounced/unsubscribed status — those auto-exclude. Reserve contacts delete --confirm for genuine removal.

4

### rename merges tags

Renaming a tag to an existing name merges them. Handy for cleaning up 'founder' vs 'founders' — but it's not reversible, so check the target name first.

## Frequently asked questions

How are contacts scoped?

To an organization and workspace. Every contact command takes `--org` and `--workspace`; the same contact IDs won't resolve in a different workspace.

How do I avoid duplicate contacts on import?

Imports de-duplicate by email server-side, so re-importing the same people adds them to the list without creating copies. Pass `--idempotency-key` so the import job itself is also safe to retry.

What's the difference between lists and tags?

**Lists** define the audience a campaign sends to; **tags** are free-form labels for filtering and segmentation. A contact belongs to lists and can carry many tags.

How do I remove a tag everywhere?

`contact-tags delete --confirm` removes it from all contacts, and `contact-tags rename` merges one tag into another. Both are destructive, so delete requires `--confirm`.

Can I export my contacts?

Yes — use `contact-exports` to pull contacts back out as CSV. In the app, export reflects your current filter/selection; via the API you work with export jobs.

Should I delete contacts that bounce?

Usually no. Bounced, unsubscribed, and cleaning-flagged (dirty) contacts are auto-excluded from sends, so deletion is rarely needed. Hard-delete only when you truly want the record gone, and it requires `--confirm`.

## Ready to put this into practice?

Start your FirstSales trial and launch a warmed, authenticated mailbox in minutes.

[Start for $1](https://app.firstsales.io)

[All tutorials](/tutorial/)