Parallax + Zapier
A step-by-step guide to connecting Parallax to Zapier and building your first working Zaps — no help from the Parallax team required.
Who this is for: anyone in operations, resourcing, or delivery who wants changes in Parallax to automatically create or update records in another tool (most commonly Asana).
Contents
- What the integration does
- Before you start
- Step 1 — Create your Parallax API key
- Step 2 — Connect Parallax in Zapier
- Step 3 — Pick a trigger
- The data each trigger gives you
- The one Parallax action
- Recipes: the Asana sync, Zap by Zap
- Keeping your task usage down
- Turning Zaps on and off
- Troubleshooting
- What the integration cannot do
What the integration does
The integration watches for changes to Project Offerings and Project Offering Roles in Parallax and hands them to Zapier in real time. From there you can do anything Zapier can do — create an Asana project, post to Slack, add a row to a sheet.
It is deliberately narrow:
- It listens. Six triggers fire when an Offering or an Offering Role is created, updated, or deleted.
- It reads. Every trigger arrives pre-filled with the surrounding context — the parent Project, the Client, the assigned Person — so you almost never need a lookup step.
- It writes one thing. A single action stores an outside system's ID (for example an Asana project ID) on a Parallax Offering, so the two records stay linked.
Everything else — creating projects, adding people, logging time — stays in Parallax. See What the integration cannot do.
The mental model
The integration is built around a one-to-one mapping:
| In Parallax | In your other tool (e.g. Asana) |
|---|---|
| Project Offering | one Project |
| Project Offering Role | one Task inside that Project |
| Person assigned to a Role | that Task's assignee |
If your workflow fits that shape, the recipes below will work almost as written.
Before you start
You'll need:
- A Parallax account that can see the Projects and Offerings you want to automate. The integration sees exactly what this user sees — no more. If your Zaps need to cover the whole organization, use an account with organization-wide visibility rather than one scoped to a few projects.
- Permission to generate a Personal Access Token in Parallax.
- A Zapier account on a plan that supports premium apps and multi-step Zaps.
- An account in the destination tool (Asana, Slack, etc.) already connected to Zapier.
Step 1 — Create your Parallax API key
- Sign in to Parallax as the user the integration should act as (see above — visibility matters).
- Go to Settings → Personal Access Tokens, or open app.getparallax.com/settings/personal-access-tokens directly.
- Click Generate Token and name it something you'll recognize later, e.g. Zapier — Asana sync.
- Copy the token immediately. It's shown only once. If you lose it, generate a new one — there's no way to view an existing token again.
Treat the token like a password. Anyone holding it can read everything that Parallax user can read.
Step 2 — Connect Parallax in Zapier
- Start a new Zap and search for Parallax as the trigger app.
- Pick any trigger, then click Sign in on the account step.
- Paste your token into the API Key field and click Yes, Continue.
Zapier immediately tests the token against Parallax. If it succeeds, the connection is saved and reusable across every Zap you build.
If the test fails:
| Message | What it means | What to do |
|---|---|---|
| Invalid or expired API key | Parallax rejected the token | Check for stray spaces when pasting. If it still fails, generate a fresh token — and confirm you were signed into the right Parallax organization when you generated it. |
| Any other error | Parallax returned an unexpected error | Usually temporary. Wait a minute and retry. If it persists, contact Parallax support with the error Zapier shows. |
You can add more than one Parallax connection (for example one per organization) and choose per Zap which one to use.
Step 3 — Pick a trigger
All six triggers are real-time. Parallax pushes the event to Zapier the moment it happens; there's no polling delay and no schedule to configure.
| Trigger | Fires when |
|---|---|
| New Project Offering | an Offering is added to a Project |
| Updated Project Offering | an Offering is edited (renamed, billing model changed, …) |
| Deleted Project Offering | an Offering is deleted |
| New Project Offering Role | a role (a staffed position) is added to an Offering |
| Updated Project Offering Role | a role is reassigned or otherwise edited |
| Deleted Project Offering Role | a role is removed from an Offering |
Two things to know before you build:
- Events only arrive while the Zap is on. Nothing that happened before you switched the Zap on will be delivered, and nothing is replayed later. To handle existing records you'll need to make a small change to each one after the Zap is live, or handle the backfill outside Zapier.
- Only Offerings and Offering Roles fire events. There is no trigger for a new Person, Project, or Client. See What the integration cannot do.
Loading sample data
When you click Test trigger during setup, Zapier can't wait around for a real event, so the integration fetches a few of your most recent Offerings or Roles and shows them as samples. These samples are shaped exactly like real events, so anything you can map from a sample will be there when a live event arrives.
If a field you expect is missing from the sample, click Test trigger again to pull a fresh set — the sample may simply have come from a record that doesn't have that field filled in yet.
The data each trigger gives you
The integration fills in the context around each event before handing it to Zapier, so you rarely need a separate lookup step. Nested records show up in Zapier's field picker with a double underscore — the Client's name is Client Name / client__name.
From the Offering triggers (New / Updated)
| Field in Zapier | What it is |
|---|---|
| Id / id | the Offering's ID |
| Offering Name (name) | the Offering name |
| billing_model, billing_model_options | how the Offering is billed |
| Asana Project ID (external_id_asana) | the linked Asana project's ID, if one has been stored (see the action) |
| ProjectId, Project Name (project__name) | the parent Project |
| project__start_date, project__end_date, project__status | the parent Project's dates and status |
| Client Name (client__name), Client ID (client__id) | the Project's Client |
| ModifiedProperties | Updated only — the list of fields that just changed, e.g. ["name"] |
| _event_occurred_on, _topic | when the change happened, and which event fired |
Note that dates live on the Project, not the Offering — use project__start_date, not a start date on the Offering itself.
From the Role triggers (New / Updated)
| Field in Zapier | What it is |
|---|---|
| Role ID (id) | the role's ID |
| Role Name (role_name) | the role's display name, e.g. Senior Developer |
| Assignee Email (person__email_address) | the assigned person's email — ideal for setting an assignee |
| Assignee Given Name, Assignee Family Name | the assigned person's name |
| Project Offering Name (project_offering__name) | the Offering this role sits under |
| Asana Project ID (external_id_asana) | the Offering's linked Asana project ID |
| Project Name (project__name), Client Name (client__name) | the surrounding Project and Client |
| ModifiedProperties | Updated only — the fields that just changed, e.g. ["assignment"] |
A role isn't always assigned to a person. When it isn't, the assignee fields come through empty — add a filter or a fallback so your Zap doesn't try to assign a task to nobody.
From the Deleted triggers
Deleted records can't be read back, so these events are thinner:
- Deleted Project Offering — IDs only (Id, ProjectId). If your cleanup step needs the Offering's name, store it in the destination tool when you create the record, so it's there when you need it.
- Deleted Project Offering Role — the role is gone, but its parent Offering survives, so you still get Project Offering Name and Asana Project ID. That's enough to find the right Asana project and clean up the task.
A note on external IDs
Parallax stores linked IDs as prefix:value pairs — asana:1209876543210123. Zapier's field picker can't reach into that, so the integration also gives you a plain, ready-to-map field per prefix:
- Use Asana Project ID (external_id_asana). It holds just the ID and drops straight into Asana's Update Project.
- external_ids (the raw list) and external_ids_by_prefix are also delivered, but they're harder to map — reach for the flat field. Note where they appear: on the Offering triggers they sit at the top level; on the Role triggers they're only nested under the Offering (project_offering__external_ids), because a role has no external IDs of its own. Only the flat fields are lifted to the top level on Role triggers.
- A flat field is produced for every prefix in the data, so a Harvest ID arrives as external_id_harvest. Only external_id_asana is guaranteed to appear in Zapier's field picker during setup, though — other prefixes, and the array and object, show up once a sampled record actually carries them.
The one Parallax action
Add External ID to Project Offering
This stores an outside system's ID on a Parallax Offering. In practice: your Zap creates an Asana project, then this action writes that Asana project's ID onto the Parallax Offering — so from then on, every Parallax event about that Offering carries the Asana ID with it.
That's what makes rename-syncing work. Without it, a later Zap would have to find the Asana project by name — which is exactly what breaks when the name is the thing that changed.
Three required fields:
| Field | Map it from |
|---|---|
| Project ID | the trigger's ProjectId |
| Offering ID | the trigger's Id (the Offering's own ID) |
| External ID | the ID from the other tool — e.g. the Asana project ID returned by the previous step |
Watch the first two. Project ID and Offering ID are both UUIDs and easy to swap. If you get "Project Offering … was not found under Project …", that's almost always what happened.
Rules for the External ID value. The step fails with a clear message if the value:
is blank,
- starts with px_ — that prefix is reserved for Parallax's own built-in integrations, so use the raw ID from the other tool, or
- contains a forward slash (/)
Safe to re-run. If the ID is already on the Offering, the step reports success and changes nothing. A sync Zap that fires repeatedly on the same Offering won't pile up errors.
The ID takes a moment to become readable
This is the single most common source of confusion, so it's worth understanding before you build.
Parallax is eventually consistent. When Add External ID reports success, the write really did land — but there's a short window afterwards in which reading the same Offering can still come back without the new ID. Anything that fires inside that window may see an Offering with an empty external_id_asana:
- another Zap whose trigger fires moments later,
- a later step in the same Zap run,
- the Offering nested on a Role event.
Nothing is broken when this happens, and nothing needs fixing in Parallax. The ID arrives shortly after; the question is only what your Zap does in the meantime.
What the integration already absorbs
The New Project Offering Role trigger waits for the parent Offering to carry an asana: ID before it builds the event — four reads spread over roughly five to nine seconds. If the ID lands in that window, the event arrives fully populated. If it doesn't, the event still goes through, just without the ID, rather than failing.
That wait is deliberately narrow. It covers one trigger and one prefix. It does not apply to:
- Updated, New, or Deleted Project Offering,
- Updated or Deleted Project Offering Role,
- any prefix other than asana,
- steps later in the same Zap run.
Everywhere else, you get whatever Parallax can see at that instant.
The workaround: put the lookup ID in the record's name
Don't make propagation timing load-bearing. When you create the record in the other tool, embed the Parallax ID in its name, so every later Zap has a matching path that doesn't depend on the external ID having propagated at all.
Names work better than notes or custom fields here because virtually every Zapier app can search on a name — which is exactly what you need from inside a trigger.
Name the Asana project in Zap 1 like this:
Acme Corp — Website Redesign — Phase 1 [px:497f6eca-6276-4993-bfeb-53cbbbba6f08]
Then any later Zap has two ways to find it:
Preferred — use external_id_asana from the trigger and address the Asana project directly by ID.
- Fallback — if that field is empty, use Asana's Find Project and search for px: plus the Offering ID from the trigger.
Wire that up with Zapier Paths, or with a Filter plus a Find Project step, so the fallback only runs when the ID is genuinely missing.
Two details that make the pattern hold up:
- Use a distinctive delimiter — [px:…] — so a name search can't collide with ordinary words in a project title.
- Keep the tag when renaming. If Zap 2 syncs Offering renames into Asana, make sure it rebuilds the name including the [px:…] tag, or the first rename will strip your fallback.
If you'd rather just wait
A Delay by Zapier step of a minute or two, placed before the step that reads the ID, will usually clear the window. It's simpler than a fallback path, but it costs an extra task per run and it's still a guess rather than a guarantee — so prefer the name-tag pattern for anything you depend on. For a one-off run that missed the ID, replaying the task from Zap History works too, since the ID will have landed by then.
Recipes: the Asana sync, Zap by Zap
This is the setup most teams start with: every Parallax Offering becomes an Asana project, and every staffed role becomes a task inside it. It's split into small Zaps so you can turn pieces on and off independently.
Build them in order — Zap 1 creates the link the later Zaps rely on.
Zap 1 — Create the Asana project when an Offering is created
Trigger: Parallax → New Project Offering
Action 1: Asana → Create Project
| Asana field | Map from |
|---|---|
| Name | Client Name + Project Name + Offering Name, then append [px: + Id + ] — combining the first three keeps projects distinguishable when several clients use the same offering name, and the [px:…] tag gives later Zaps a way to find this project even before the external ID has propagated (see above) |
| Notes | include Id (Offering ID) and ProjectId so a human can trace the Asana project back to Parallax |
| Start / due date | project__start_date / project__end_date |
Action 2: Parallax → Add External ID to Project Offering
| Field | Map from |
|---|---|
| Project ID | trigger's ProjectId |
| Offering ID | trigger's Id |
| External ID | the Asana project ID returned by Action 1 |
Action 2 is not optional — it's what lets Zaps 2, 3, and 4 find the Asana project without guessing at names.
Zap 2 — Keep the Asana project in sync when an Offering changes
Trigger: Parallax → Updated Project Offering
Filter (add this first): only continue if ModifiedProperties contains a field you care about, such as name. Parallax fires an update event on every save, so without a filter this Zap runs constantly and burns tasks for changes you don't care about.
Action: Asana → Update Project, identified by ID
| Asana field | Map from |
|---|---|
| Project ID | Asana Project ID (external_id_asana) — the ID Zap 1 stored |
| Name | the new Offering Name |
Don't match by name here. A rename is the most common update, so matching on the name is the exact case that fails. Zap 1 stored the Asana ID on the Offering precisely so this step can address the project directly.
Preserve the [px:…] tag when you rename. If you followed the naming pattern in Zap 1, rebuild the new Asana name with the tag still on the end. Mapping the bare Offering Name straight into Asana's Name field strips it — and with it the fallback later Zaps rely on when the external ID hasn't propagated yet.
Zap 3 — Create an Asana task when a role is staffed
Trigger: Parallax → New Project Offering Role
Action: Asana → Create Task
| Asana field | Map from |
|---|---|
| Project | Asana Project ID (external_id_asana) |
| Task name | Role Name |
| Assignee | Assignee Email (person__email_address) |
Add a filter if unassigned roles are common in your org: continue only when Assignee Email exists, or create the task without an assignee.
Then duplicate this Zap for Updated Project Offering Role to handle re-staffing — with a filter on ModifiedProperties containing assignment, so it only runs when the assignee actually changes. Use Asana's Update Task instead of Create Task there.
One timing note: if someone staffs a role within a few seconds of creating a brand-new Offering, Zap 1 may not have stored the Asana ID yet. This trigger is the one place the integration waits for it (roughly five to nine seconds). If it still isn't there, the event goes through without the Asana ID rather than failing. Two things cover the remainder of that gap: building the Updated Project Offering Role variant alongside this one, and the [px:…] name fallback — see The ID takes a moment to become readable. Note that the wait does not apply to the Updated variant, so that Zap in particular wants the fallback.
Zap 4 (optional) — Clean up when Parallax deletes the source
Trigger: Parallax → Deleted Project Offering Role
Action: Asana → Find Task, then Complete Task. Use Asana Project ID to scope the search to the right project and Project Offering Name for context.
For Deleted Project Offering, you only get IDs — so if you want to archive the Asana project on deletion, make sure Zap 1 wrote the Offering ID into the Asana project's notes or a custom field, and match on that.
Keeping your task usage down
Every event Zapier receives costs a task, whether or not it does anything useful. Two habits keep the bill predictable:
- Filter on ModifiedProperties in every Zap built on an Updated trigger. Parallax fires an update on each save, including trivial edits. Filtering is often the difference between a handful of runs a week and dozens.
- Only build the triggers you'll act on. A Zap that fires and then filters everything out still consumed a task.
Turning Zaps on and off
When you switch a Zap on, the integration registers with Parallax to receive that event type. When you switch it off, it unregisters.
Today Parallax supports a limited number of webhook destinations per organization, which has two practical consequences:
- Turn Zaps on one at a time, and confirm each one works (check Zap History) before enabling the next.
- If you turn one Parallax Zap off, re-check your others. Occasionally another Zap's registration is dropped along with it. The fix is simple: switch the affected Zap off and back on to re-register it.
If events start landing on the wrong Zap, see Troubleshooting below.
Troubleshooting
The Zap never fires
Work through these in order:
- Is the Zap on? Events are only delivered while it's on, and nothing is replayed for the time it was off.
- Did the change actually fire that event? Only Offerings and Offering Roles produce events. Editing the Project itself, or the Client, produces nothing.
- Can your token's user see that record? The integration only sees what the Parallax user who generated the token can see. Make a test change on a Project you're certain that user has access to.
- Was the Zap recently toggled? Switch it off and back on to re-register (see above).
Events are showing up on the wrong Zap
If you have several Parallax Zaps on the same connection and deliveries seem to be going to the wrong one, turn them all off, then switch them on one at a time, checking Zap History after each. If the problem tracks to two Zaps watching the same event type, run just one of them, or split them across separate Parallax organizations using separate connections.
A step fails with 401 or 403
The token is being rejected. Generate a fresh token in Parallax and update the connection in Zapier. If a new token also fails, check that the Parallax user is still active and still in the expected organization — tokens belong to a user, so deactivating that user invalidates their tokens.
Some records work, others 403 or come through with blank Project/Client
That's a visibility gap, not a bug. The token's user can see some Projects but not others. Either grant that user access to the missing Projects, or reconnect using an account with broader visibility.
"Project Offering … was not found under Project …"
The Add External ID step got a Project ID and Offering ID that don't go together — nearly always because the two fields are swapped. Check that Project ID maps to ProjectId and Offering ID maps to Id.
Zap History shows the event was dropped
The integration looks up the full record when an event arrives. If the record has already been deleted by then — an edit and a delete landing at nearly the same moment — there's nothing left to act on, so the event is dropped rather than failed. This is expected, and the matching Deleted trigger fires separately to handle the removal.
If a record you expect to process is dropped consistently, it's more likely a visibility problem (see above) than a deletion.
The Asana Project ID is empty on a trigger that should have it
Two different causes, and they need different fixes:
- The Offering never had one. Add External ID hasn't run for it — most often an Offering created before you switched Zap 1 on. Re-save the Offering in Parallax to fire a fresh event, or add the ID manually.
- It has one, but the event fired too soon after the write-back. This is the propagation window described in The ID takes a moment to become readable. Add the [px:…] name fallback rather than trying to tune the timing — the same run will succeed on a replay, which is a good way to confirm this is the cause.
A field is missing from the field picker
Click Test trigger again to pull a fresh sample. Samples come from your real recent records, so a field can be absent simply because the sampled record didn't have it — a newly created Offering, for instance, has no Asana ID until Zap 1 has run once.
Still stuck
Contact Parallax support with:
- the Zap's name and the trigger you used,
- a link to the failing run in Zap History, and
- the exact error text Zapier displays.
What the integration cannot do
Worth knowing up front, so you don't design a workflow around something that isn't there.
| You might want | Available? | Do this instead |
|---|---|---|
| A trigger for a new Person | No | Trigger from your HRIS. Person details for a staffed role already arrive on the Role triggers. |
| A trigger for a new or changed Project | No | Watch New Project Offering — every billable project has at least one Offering. |
| A trigger for a new Client | No | Client details arrive alongside Offering and Role events. |
| To create or update a Project, Offering, Person, or Client from Zapier | No | These stay in the Parallax UI. The only write is Add External ID. |
| To log time / create Actual Time entries | No | Not supported by the integration. |
| To search Parallax by name | No | Everything you need arrives on the trigger already. |
| To process records that already exist | No | Triggers only deliver events that happen after the Zap is on. Touch each record to generate an event, or handle the backfill outside Zapier. |
| A scheduled / polling trigger | No | All triggers are real-time. |
If your workflow genuinely needs one of these, let the Parallax team know rather than building a workaround — the integration is intentionally narrow right now, and this feedback shapes what ships next.